I'm using csrfMagic as an automatic CSRF protection for my project. when I include the script in my project XMLHTTPRequest events don't work.
for example 'onprogress' event is not triggered.
my code:
var xhr = new XMLHttpRequest()
xhr.open("POST", "./");
xhr.onreadystatechange = function (e) {
//some code
}
xhr.upload.onprogress = function (e) {
//this event is not triggered
console.log('xhr.upload.onprogress was triggerd');
}
csrfMagic script:
/**
* #file
*
* Rewrites XMLHttpRequest to automatically send CSRF token with it. In theory
* plays nice with other JavaScript libraries, needs testing though.
*/
// Here are the basic overloaded method definitions
// The wrapper must be set BEFORE onreadystatechange is written to, since
// a bug in ActiveXObject prevents us from properly testing for it.
CsrfMagic = function(real) {
// try to make it ourselves, if you didn't pass it
if (!real) try { real = new XMLHttpRequest; } catch (e) {;}
if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) {;}
if (!real) try { real = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {;}
if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP.4.0'); } catch (e) {;}
this.csrf = real;
// properties
var csrfMagic = this;
real.onreadystatechange = function() {
csrfMagic._updateProps();
return csrfMagic.onreadystatechange ? csrfMagic.onreadystatechange() : null;
};
csrfMagic._updateProps();
}
CsrfMagic.prototype = {
open: function(method, url, async, username, password) {
if (method == 'POST') this.csrf_isPost = true;
// deal with Opera bug, thanks jQuery
if (username) return this.csrf_open(method, url, async, username, password);
else return this.csrf_open(method, url, async);
},
csrf_open: function(method, url, async, username, password) {
if (username) return this.csrf.open(method, url, async, username, password);
else return this.csrf.open(method, url, async);
},
send: function(data) {
if (!this.csrf_isPost) return this.csrf_send(data);
prepend = csrfMagicName + '=' + csrfMagicToken + '&';
if (this.csrf_purportedLength === undefined) {
this.csrf_setRequestHeader("Content-length", this.csrf_purportedLength + prepend.length);
delete this.csrf_purportedLength;
}
delete this.csrf_isPost;
return this.csrf_send(prepend + data);
},
csrf_send: function(data) {
return this.csrf.send(data);
},
setRequestHeader: function(header, value) {
// We have to auto-set this at the end, since we don't know how long the
// nonce is when added to the data.
if (this.csrf_isPost && header == "Content-length") {
this.csrf_purportedLength = value;
return;
}
return this.csrf_setRequestHeader(header, value);
},
csrf_setRequestHeader: function(header, value) {
return this.csrf.setRequestHeader(header, value);
},
abort: function() {
return this.csrf.abort();
},
getAllResponseHeaders: function() {
return this.csrf.getAllResponseHeaders();
},
getResponseHeader: function(header) {
return this.csrf.getResponseHeader(header);
} // ,
}
// proprietary
CsrfMagic.prototype._updateProps = function() {
this.readyState = this.csrf.readyState;
if (this.readyState == 4) {
this.responseText = this.csrf.responseText;
this.responseXML = this.csrf.responseXML;
this.status = this.csrf.status;
this.statusText = this.csrf.statusText;
}
}
CsrfMagic.process = function(base) {
var prepend = csrfMagicName + '=' + csrfMagicToken;
if (base) return prepend + '&' + base;
return prepend;
}
// callback function for when everything on the page has loaded
CsrfMagic.end = function() {
// This rewrites forms AGAIN, so in case buffering didn't work this
// certainly will.
forms = document.getElementsByTagName('form');
for (var i = 0; i < forms.length; i++) {
form = forms[i];
if (form.method.toUpperCase() !== 'POST') continue;
if (form.elements[csrfMagicName]) continue;
var input = document.createElement('input');
input.setAttribute('name', csrfMagicName);
input.setAttribute('value', csrfMagicToken);
input.setAttribute('type', 'hidden');
form.appendChild(input);
}
}
// Sets things up for Mozilla/Opera/nice browsers
// We very specifically match against Internet Explorer, since they haven't
// implemented prototypes correctly yet.
if (window.XMLHttpRequest && window.XMLHttpRequest.prototype && '\v' != 'v') {
var x = XMLHttpRequest.prototype;
var c = CsrfMagic.prototype;
// Save the original functions
x.csrf_open = x.open;
x.csrf_send = x.send;
x.csrf_setRequestHeader = x.setRequestHeader;
// Notice that CsrfMagic is itself an instantiatable object, but only
// open, send and setRequestHeader are necessary as decorators.
x.open = c.open;
x.send = c.send;
x.setRequestHeader = c.setRequestHeader;
} else {
// The only way we can do this is by modifying a library you have been
// using. We support YUI, script.aculo.us, prototype, MooTools,
// jQuery, Ext and Dojo.
if (window.jQuery) {
// jQuery didn't implement a new XMLHttpRequest function, so we have
// to do this the hard way.
jQuery.csrf_ajax = jQuery.ajax;
jQuery.ajax = function( s ) {
if (s.type && s.type.toUpperCase() == 'POST') {
s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
if ( s.data && s.processData && typeof s.data != "string" ) {
s.data = jQuery.param(s.data);
}
s.data = CsrfMagic.process(s.data);
}
return jQuery.csrf_ajax( s );
}
}
if (window.Prototype) {
// This works for script.aculo.us too
Ajax.csrf_getTransport = Ajax.getTransport;
Ajax.getTransport = function() {
return new CsrfMagic(Ajax.csrf_getTransport());
}
}
if (window.MooTools) {
Browser.csrf_Request = Browser.Request;
Browser.Request = function () {
return new CsrfMagic(Browser.csrf_Request());
}
}
if (window.YAHOO) {
// old YUI API
YAHOO.util.Connect.csrf_createXhrObject = YAHOO.util.Connect.createXhrObject;
YAHOO.util.Connect.createXhrObject = function (transaction) {
obj = YAHOO.util.Connect.csrf_createXhrObject(transaction);
obj.conn = new CsrfMagic(obj.conn);
return obj;
}
}
if (window.Ext) {
// Ext can use other js libraries as loaders, so it has to come last
// Ext's implementation is pretty identical to Yahoo's, but we duplicate
// it for comprehensiveness's sake.
Ext.lib.Ajax.csrf_createXhrObject = Ext.lib.Ajax.createXhrObject;
Ext.lib.Ajax.createXhrObject = function (transaction) {
obj = Ext.lib.Ajax.csrf_createXhrObject(transaction);
obj.conn = new CsrfMagic(obj.conn);
return obj;
}
}
if (window.dojo) {
// NOTE: this doesn't work with latest dojo
dojo.csrf__xhrObj = dojo._xhrObj;
dojo._xhrObj = function () {
return new CsrfMagic(dojo.csrf__xhrObj());
}
}
}
Your code does not contain a call of send method of XMLHttpRequest. So csrf_magic can't add csrf token to HTTP-request.
Example of a correct upload function:
function upload(file) {
var xhr = new XMLHttpRequest();
xhr.upload.onprogress = function(event) {
log(event.loaded + ' / ' + event.total);
}
xhr.onload = xhr.onerror = function() {
if (this.status == 200) {
log("success");
} else {
log("error " + this.status);
}
};
xhr.open("POST", "upload", true);
xhr.send(file);
}
Links:
https://developer.mozilla.org/ru/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
Related
I've seen lots of variations on this question, but none of them applies to this specific situation. I'm a bit confused because of all the data and object types.
Consider the following code in JavaScript:
function postRequest(url, params, success, error, keepactive = 1)
{
let req = false;
try
{
// most browsers
req = new XMLHttpRequest();
} catch (e)
{
// IE
try
{
req = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e)
{
// try an older version
try
{
req = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e)
{
return false;
}
}
}
if (keepactive === 0)
{
ajaxcalls.push(req);
console.log(ajaxcalls.length + ' calls active');
} else if (keepactive === 2)
{
console.log('Filter call: ' + ajaxcalls.length + ' calls active');
filtercall = req;
} else if (keepactive === 3)
{
console.log('Jump call: ' + ajaxcalls.length + ' calls active');
jumpcall = req;
}
if (!req) return false;
if (typeof success != 'function') success = function ()
{
};
if (typeof error != 'function') error = function ()
{
};
req.onreadystatechange = function ()
{
if (req.readyState === 4)
{
// Success! Remove req from active calls.
if (keepactive === 0)
{
for (let i = ajaxcalls.length - 1; i >= 0; i--)
{
if (ajaxcalls[i] === req)
{
ajaxcalls.splice(i, 1);
console.log(ajaxcalls.length + ' calls active');
}
}
}
return req.status === 200 ?
success(req.responseText) : error(req.status);
// dus eigenlijk displayUpdateWorksheet(req.responseText)
// dus eigenlijk displayUpdateWorksheetError(req.status)
}
}
req.open("POST", url, true);
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.send(params);
return req;
}
function uploadPicture(myindex, stringid)
{
let file = document.getElementById("file").files[0];
let contents = new FileReader(); // no arguments
contents.readAsDataURL(file);
contents.onload = function ()
{
// console.log(contents.result);
let filename;
let client;
let field = myindex;
let filecontainer = document.getElementById('file' + myindex);
if (filecontainer != null)
{
filename = filecontainer.innerHTML.replace(/\+/g, '+').replace(/&/g, '&').replace(/#/g, '#').replace(/%/g, '%');
}
let clientcontainer = document.getElementById('myclient');
if (clientcontainer != null)
{
client = clientcontainer.innerHTML.replace(/\+/g, '+').replace(/&/g, '&').replace(/#/g, '#').replace(/%/g, '%');
}
alert('Picture uploaded!');
// let post_array = { client: client, stringid: stringid, filename: filename, field: field, contents: contents.result }
postRequest(
'ajaxcalls/ajaxUploadPicture.php', // url
'&client=' + client +
'&stringid=' + stringid +
'&filename=' + filename +
'&field=' + myindex +
'&contents=' + contents.result +
'&type=' + file.type,
function (responseText)
{
return drawOutputUploadPicture(myindex, responseText);
}, // success
drawErrorUploadPicture // error
);
}
contents.onerror = function ()
{
console.log(contents.error);
};
};
And the following PHP:
$data = $_POST['contents'];
$contents = preg_replace('#data:image/[^;]+;base64,#', '', $data);
$contents = base64_decode($contents);
...
file_put_contents($file_full, $contents);
($file_full is correct here: the file is saved at the right spot. I've just cut some irrelevant code there: nothing in $contents is changed after this.)
When I upload a 52K image, I lose about 1K and the resulting image cannot be opened. What's going on?
The main issue is that 1) I'm using POST with 2) multiple variables and 3) Vanilla JavaScript. I've seen no working examples of that.
When I open the original and copy as text, the beginning is the same:
Original: ���� JFIF ` ` ���Exif
Copy: ���� JFIF ` ` ���Exif
Then a series of NUL's in both files. Then the files start deviating:
Original:
2021:07:22 16:21:52 2021:07:22 16:21:52 L o e k v a n K o o t e n ��"http://ns.adobe.com/xap/1.0/ <?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?>
<x:xmpmeta xmlns:x="adobe:ns:meta/"><rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:Description rdf:about="uuid:faf5bdd5-ba3d-11da-ad31-d33d75182f1b" xmlns:dc="http://purl.org/dc/elements/1.1/"/><rdf:Description rdf:about="uuid:faf5bdd5-ba3d-11da-ad31-d33d75182f1b" xmlns:xmp="http://ns.adobe.com/xap/1.0/"><xmp:CreateDate>2021-07-22T16:21:52.056</xmp:CreateDate></rdf:Description><rdf:Description rdf:about="uuid:faf5bdd5-ba3d-11da-ad31-d33d75182f1b" xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:creator><rdf:Seq xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><rdf:li>Loek van Kooten</rdf:li></rdf:Seq>
</dc:creator></rdf:Description></rdf:RDF></x:xmpmeta>
Copy:
2021:07:22 16:21:52 2021:07:22 16:21:52 L o e k v a n K o o t
e n
�BȚ��ۜ˘YؙK���K�\�K���X��]�Y�[�I�����YI��SL\�ZR��Tޓ�ޚ��Y �σB��\Y]H[�ΞH�YؙN��ΛY]Kȏ������[�Μ��H������˝�˛ܙ��NNNK��̌�\��\�[�^[��ȃ�&Fc�FW67&�F���&Fc�&�WC�'WV�C�fcV&FCR�&6B�F�C3�C36CsS�&c""����3�F3�&�GG���W&���&r�F2�V�V�V�G2���"���&Fc�FW67&�F���&Fc�&�WC�'WV�C�fcV&FCR�&6B�F�C3�C36CsS�&c""����3����&�GG����2�F�&R�6�������
���� ɕ�ѕ�є����Ĵ�ܴ��P�������ȸ�������
ɕ�ѕ�є��ɑ���͍ɥ�ѥ����ɑ���͍ɥ�ѥ���ɑ�酉�����ե�际�Չ��Ե��͐��ő�����ĵ��͐����ɘň��ᵱ��鑌����輽��ɰ��ɜ�����������̼ĸļ�<dc:creator����\H[�Μ��H������˝�˛ܙ��NNNK��̌�\��\�[�^[��ȏ����O��Z��[����[�ܙ��O�ܙ���\O�B�BBO�ΘܙX]�ܙ��\�ܚ[ۏ�ܙ�����������WF4
Anyone recognizes what's going on here?
For some reason, in this case $_POST['contents'] contains spaces. If I replace these with + the image comes out nicely. I imagine that when a file is sent as a $_POST variable, it gets split with a space every so many characters.
I want to upload an image file to server and then show it on browser editor on return.
For that, I have #fileInput form input (type file) to upload an image to server.
On change #fileInput, I trigger uploadAndReadURLfunction which calls app.uploader for upload.
When upload is finished, it returns to line commented "Coming here" below. However, I want it to return to the line commented "Not coming here". How can I make this happen.
var app = app || {};
(function(o) {
"use strict";
var ajax, getFormData;
ajax = function(data) {
var xmlhttp = new XMLHttpRequest(), uploaded;
xmlhttp.addEventListener('readystatechange', function() {
if(this.readyState === 4) {
if(this.status === 200) {
var res =this.response;
if(res == 1) {
console.log(res); // Coming here.
}
}
}
});
xmlhttp.open('post', o.options.processor);
xmlhttp.send(data);
};
getFormData = function(source) {
var data = new FormData(), i;
for(i = 0; i < source.files.files.length; i = i + 1) {
data.append('file[]', source.files.files[i]);
}
data.append('ajax', true);
return data;
};
o.uploader = function(options) {
o.options = options;
if(o.options.files !== undefined) {
ajax(getFormData(o.options));
}
}
}(app));
function uploadAndReadURL(input) {
if(input.files && input.files[0]) {
var f = document.getElementById('fileInput');
app.uploader({
files: f,
processor: "/geornal/image",
finished: function(data) {
console.log("burada2."); // Not coming here..
},
error: function() {
console.log('Not working');
}
});
}
}
$(document).ready(function(){
$("#icerik2").on("change", "#fileInput", function(){
uploadAndReadURL(this);
});
});
There is "finished:" section in uploadAndReadURL function. I don't
know how to call "finished" from app function.
Try calling o.options.finished() at if statement within readystatechange handler
if(res == 1) { o.options.finished(res); }
I'm trying to implement a feature that will give me the response text and (all the) response headers of each http request in the browser,
I've read a bit here: http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/
I had no idea how to extract the response from the TracingListener object. Anyone dealt with that before?? thanks!
My code:
const Cc = Components.classes;
const Ci = Components.interfaces;
// Helper function for XPCOM instanciation (from Firebug)
function CCIN(cName, ifaceName) {
return Cc[cName].createInstance(Ci[ifaceName]);
}
// get the observer service and register for the two coookie topics.
var observerService = Components.classes["#mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
// Copy response listener implementation.
function TracingListener() {
this.originalListener = null;
this.receivedData = []; // array for incoming data.
}
TracingListener.prototype =
{
originalListener: null,
receivedData: null, // array for incoming data.
onDataAvailable: function(request, context, inputStream, offset, count)
{
var binaryInputStream = CCIN("#mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream");
var storageStream = CCIN("#mozilla.org/storagestream;1", "nsIStorageStream");
binaryInputStream.setInputStream(inputStream);
storageStream.init(8192, count, null);
var binaryOutputStream = CCIN("#mozilla.org/binaryoutputstream;1",
"nsIBinaryOutputStream");
binaryOutputStream.setOutputStream(storageStream.getOutputStream(0));
// Copy received data as they come.
var data = binaryInputStream.readBytes(count);
this.receivedData.push(data);
binaryOutputStream.writeBytes(data, count);
this.originalListener.onDataAvailable(request, context,storageStream.newInputStream(0), offset, count);
},
onStartRequest: function(request, context) {
this.originalListener.onStartRequest(request, context);
},
onStopRequest: function(request, context, statusCode)
{
try {
if (request.originalURI && piratequesting.baseURL == request.originalURI.prePath && request.originalURI.path.indexOf("/index.php?ajax=") == 0) {
dump("\nProcessing: " + request.originalURI.spec + "\n");
var date = request.getResponseHeader("Date");
var responseSource = this.receivedData.join();
dump("\nResponse: " + responseSource + "\n");
piratequesting.ProcessRawResponse(request.originalURI.spec, responseSource, date);
}
} catch(e) { dumpError(e);}
this.originalListener.onStopRequest(request, context, statusCode);
},
QueryInterface: function (aIID) {
if (aIID.equals(Ci.nsIStreamListener) ||
aIID.equals(Ci.nsISupports)) {
return this;
}
throw Components.results.NS_NOINTERFACE;
}
}
// create an nsIObserver implementor
var listener = {
observe : function(aSubject, aTopic, aData) {
// Make sure it is our connection first.
//if (aSubject == gChannel) {
var httpChannel = aSubject.QueryInterface(Components.interfaces.nsIHttpChannel);
if (aTopic == "http-on-modify-request") {
// ...
httpChannel.setRequestHeader("X-Hello", "World", false);
} else if (aTopic == "http-on-examine-response") {
// ...
//alert(JSON.stringify(aData));
var newListener = new TracingListener();
aSubject.QueryInterface(Ci.nsITraceableChannel);
newListener.originalListener = aSubject.setNewListener(newListener);
alert(httpChannel.getResponseHeader("Location"));
//alert(httpChannel.getAllResponseHeaders());
}
//}
},
QueryInterface : function(aIID) {
if (aIID.equals(Components.interfaces.nsISupports) ||
aIID.equals(Components.interfaces.nsIObserver))
return this;
throw Components.results.NS_NOINTERFACE;
}
};
observerService.addObserver(listener, "http-on-modify-request", false);
observerService.addObserver(listener, "http-on-examine-response", false);
I have defined one root object that I want to use as "namespace" for the rest of my classes. Inside this root object i have two classes - TOOLS and PRESENTATION. In PRESENTATION class i need to call one of public methods from TOOLS. As I can tell after playing with console.log in every step of execution of this code problem is that return xhr.responseText don't pass anything back to tools.getData(configPath) and I'm ending up with undefined in console.log(pres.config).
CODE:
// Create Namespace
var AppSpace = AppSpace || {};
// Class and Constructor
AppSpace.Tools = function() {
//public methodts
this.test = function(arg) {
return arg
}
this.getData = function(path) {
var xhr = new XMLHttpRequest();
xhr.open('GET', path, false);
xhr.onreadystatechange = function() {
if (xhr.readyState !== 4) return;
if (xhr.status !== 0 && xhr.status !== 200) {
if (xhr.status === 400) {
console.log("Could not locate " + path);
} else {
console.error("app.getData " + path + " HTTP error: " + xhr.status);
}
return;
}
return xhr.responseText;
};
xhr.send();
}
}
// Class and Constructor
AppSpace.Presentation = function(initName, initfPath){
//private properties
var configPath = initfPath || 'config.json';
var configData = null;
var name = initName || 'Unnamed Presentation';
//private methods
var getConfigContent = function() {
return tools.getData(configPath);
}
var getConfigData = function() {
return JSON.parse(getConfigContent);
}
//public methodts
//public properties
this.name = null;
this.config = null;
this.debug = null;
//logic
this.name = name;
this.config = getConfigContent();
}
//execution
var tools = new AppSpace.Tools();
var pres = new AppSpace.Presentation('Some Name');
pres.debug = tools.test('value passed')
console.log(pres.debug);
console.log(pres.config);
console.log(pres.name);
Output in browsers console is:
value passed js-oop.dev:99
**undefined js-oop.dev:100**
Some Name js-oop.dev:101
Can anyone give little advice on this? TIA.
I mean that if you want that your ajax control return some datas directly from your function you have to use a synchronous method. Without it, datas will be sent from the onreadystatechange event.
Here is a sample how to create a synchronous call for ajax
// Create Namespace
var AppSpace = AppSpace || {};
// Class and Constructor
AppSpace.Tools = function() {
//public methodts
this.test = function(arg) {
return arg
}
this.getData = function(path) {
var xhr_object= new XMLHttpRequest();
// Start synchronous ajax
xhr_object.open("GET", path, false);
xhr_object.send(null);
// Return data
return xhr_object.responseText;
}
}
// Class and Constructor
AppSpace.Presentation = function(initName, initfPath){
//private properties
var configPath = initfPath || 'config.json';
var configData = null;
var name = initName || 'Unnamed Presentation';
//private methods
var getConfigContent = function() {
return tools.getData(configPath);
}
var getConfigData = function() {
return JSON.parse(getConfigContent);
}
//public methodts
//public properties
this.name = null;
this.config = null;
this.debug = null;
//logic
this.name = name;
this.config = getConfigContent();
}
//execution
var tools = new AppSpace.Tools();
var pres = new AppSpace.Presentation('Some Name');
pres.debug = tools.test('value passed')
console.log(pres.debug);
console.log(pres.config);
console.log(pres.name);
First, in this code:
this.getData = function(path) {
var xhr = new XMLHttpRequest();
xhr.open('GET', path, false);
xhr.onreadystatechange = function() {
if (xhr.readyState !== 4) return;
if (xhr.status !== 0 && xhr.status !== 200) {
if (xhr.status === 400) {
console.log("Could not locate " + path);
} else {
console.error("app.getData " + path + " HTTP error: " + xhr.status);
}
return;
}
return xhr.responseText;
};
xhr.send();
}
return xhr.responseText; will not work. It is inside handler function and value will be returned from xhr.onreadystatechange, but not from getData, so you may do something like this:
this.getData = function(path) {
var res;
var xhr = new XMLHttpRequest();
xhr.open('GET', path, false);
xhr.onreadystatechange = function() {
if (xhr.readyState !== 4) return;
if (xhr.status !== 0 && xhr.status !== 200) {
if (xhr.status === 400) {
console.log("Could not locate " + path);
} else {
console.error("app.getData " + path + " HTTP error: " + xhr.status);
}
return;
}
res = xhr.responseText;
};
xhr.send();
return res;
}
Also, this should be like next (you are trying to parse an function, not what it returns)
var getConfigData = function() {
return JSON.parse(getConfigContent());
}
If you'd like to keep it asynchrous you can do something like this. I'd either remove the dest and prop parameters, or the callback, depending on your needs.
this.getData = function(path, dest, prop, callback) {
callback = callback || null;
var xhr = new XMLHttpRequest();
xhr.open('GET', path, false);
xhr.onreadystatechange = function() {
if (xhr.readyState !== 4) return;
if (xhr.status !== 0 && xhr.status !== 200) {
/* ... */
}
dest[prop] = xhr.responseText;
if (Callback) callback(xhr.responseText);
};
xhr.send();
}
//private methods
var getConfigContent = function() {
return tools.getData(configPath, this, 'config', );
}
I'm developing a simple auxiliary class to send requests using XmlHttpRequest (code below). But I cant make it work. At google chrome, for example, I get the error INVALID_STATE_ERR: DOM Exception 11 and at the other browsers I get a status == 0.
//#method XRequest: Object constructor. As this implements a singleton, the object can't be created calling the constructor, GetInstance should be called instead
function XRequest() {
this.XHR = XRequest.CreateXHR();
}
XRequest.instance = null;
//#method static GetInstance: Creates a singleton object of type XRequest. Should be called whenever an object of that type is required.
//#return: an instance of a XRequest object
XRequest.GetInstance = function() {
if(XRequest.instance == null) {
XRequest.instance = new XRequest();
}
return XRequest.instance;
}
//#method static CreateXHR: Implments a basic factory method for creating a XMLHttpRequest object
//#return: XMLHttp object or null
XRequest.CreateXHR = function() {
var xhr = null;
var factory = [
function() { return new XMLHttpRequest(); },
function() { return new ActiveXObject("Msxml2.XMLHTTP"); },
function() { return new ActiveXObject("Microsoft.XMLHTTP"); }
];
for(var i = 0; i < factory.length; ++i) {
var f = factory[i];
xhr = f();
if(xhr) return xhr;
}
return null;
}
XRequest.prototype.SetRequestHeader = function(name, value) {
if(this.XHR) {
this.XHR.setRequestHeader(name, value);
}
}
XRequest.prototype.SendRequest = function(args) {
var async = true;
var type = "";
var url = "";
var username = "";
var password = "";
var body = null;
var success = null;
var failure = null;
for(e in args) {
switch(e) {
case "async":
async = args[e];
break;
case "type":
type = args[e];
break;
case "success":
success = args[e];
break;
case "failure":
failure = args[e];
break;
case "url":
url = args[e];
break;
case "username":
username = args[e];
break;
case "password":
password = args[e];
break;
case "body":
body = args[e];
break;
case "setHeader":
var h = args[e].split(":");
if(h.length == 2) {
this.SetRequestHeader(h[0], h[1]);
}
break;
}
}
var that = this;
this.XHR.onreadystatechange = function() {
alert("readyState == " + that.XHR.readyState + " status == " + that.XHR.status);
if(that.XHR.readyState == 4) {
if(that.XHR.status == 200 || that.XHR.status == 0) {
if(success) success(that.XHR);
} else {
if(failure) failure();
}
}
};
this.XHR.open(type, url, async, username, password);
this.XHR.send(body);
}
Example of usage:
<script language="javascript">
function onLoad() {
var x = XRequest.GetInstance();
x.SendRequest({type:"GET",
setHeader:"Accept:text/html, image/png, image/*, */*",
url: "http://your_server.com/getData?param1=test",
success:onSuccess, failure:onFail
});
}
function onSuccess(obj) {
alert("OK");
}
function onFail() {
alert("Not at this time!");
}
</script>
Problem in this ajax library.
XHR.setRequestHeader() must be called after XHR.open().
// #method XRequest: Object constructor. As this implements a singleton, the object can't be created calling the constructor, GetInstance should be called instead
function XRequest()
{
this.XHR = XRequest.CreateXHR();
}
XRequest.instance = null;
// #method static GetInstance: Creates a singleton object of type XRequest. Should be called whenever an object of that type is required.
// #return: an instance of a XRequest object
XRequest.GetInstance = function()
{
if(XRequest.instance == null)
{
XRequest.instance = new XRequest();
}
return XRequest.instance;
}
// #method static CreateXHR: Implments a basic factory method for creating a XMLHttpRequest object
// #return: XMLHttp object or null
XRequest.CreateXHR = function()
{
var xhr = null;
var factory = [
function() { return new XMLHttpRequest(); },
function() { return new ActiveXObject("Msxml2.XMLHTTP"); },
function() { return new ActiveXObject("Microsoft.XMLHTTP"); }
];
for(var i = 0; i < factory.length; ++i)
{
var f = factory[i];
xhr = f();
if(xhr)
return xhr;
}
return null;
}
XRequest.prototype.SetRequestHeader = function(name, value)
{
if(this.XHR)
{
//alert(name+'|||'+value);
this.XHR.setRequestHeader(name, value);
}
}
XRequest.prototype.SendRequest = function(args)
{
var async = true;
var type = "";
var url = "";
var username = "";
var password = "";
var body = null;
var success = null;
var failure = null;
for(e in args)
{
switch(e)
{
case "async":
async = args[e];
break;
case "type":
type = args[e];
break;
case "success":
success = args[e];
break;
case "failure":
failure = args[e];
break;
case "url":
url = args[e];
break;
case "username":
username = args[e];
break;
case "password":
password = args[e];
break;
case "body":
body = args[e];
break;
}
}
var that = this;
this.XHR.onreadystatechange = function()
{
alert("readyState == " + that.XHR.readyState + " status == " + that.XHR.status);
if(that.XHR.readyState == 4)
{
if(that.XHR.status == 200 || that.XHR.status == 0)
{
if(success)
success(that.XHR);
}
else
{
if(failure)
failure();
}
}
};
this.XHR.open(type, url, async, username, password);
for(e in args)
{
switch(e)
{
case "setHeader":
var h = args[e].split(":");
if(h.length == 2)
{
this.SetRequestHeader(h[0], h[1]);
}
break;
}
}
this.XHR.send(body);
}
Regardless, you can simplify your SendRequest method by creating a mixin instead of using a giant switch.
XRequest.prototype.SendRequest = function(params) {
var defaultParams = {
async: true,
type: "",
url: "",
username: "",
password: "",
body: null,
success: null,
failure: null
};
for ( var i in defaultParams ) {
if ( defaultParams.hasOwnProperty(i) && typeof params[i] == "undefined" ) {
params[i] = defaultParams[i];
}
}
var that = this;
this.XHR.onreadystatechange = function() {
if ( that.XHR.readyState == 4 ) {
if ( that.XHR.status == 200 || that.XHR.status == 0 ) {
if ( params.success ) {
params.success(that.XHR);
}
} else {
if ( params.failure ) {
params.failure();
}
}
}
};
this.XHR.open(
params.type, parms.url, params.async, params.username, params.password
);
// It doesn't make sense to have a for/switch here when you're only handling
// one case
if ( params.setHeader ) {
var h = params.setHeader.split(":");
if ( h.length == 2) {
this.SetRequestHeader(h[0], h[1]);
}
}
this.XHR.send(params.body);
};
Also be careful: your existing for..in loops have two distinct problems:
You're not using var and causing a global to be created: for (e in args) should be for (var e in args)
Whenever you use for..in, you should always check to make sure that each key is a direct member of the object, and not something inherited inadvertently through prototype
.
for ( var i in obj ) {
if ( obj.hasOwnProperty(i) ) {
// do stuff here
}
}
Usually this error occurs with the XMLHttpRequest when you call the open method with async = true, or you leave the async parameter undefined so it defaults to asynchronous, and then you access the status or responseText properties. Those properties are only available after you do a synchronous call, or on the readyState becoming ready (once the asynchronous call responds). I suggest you first try with async = false, and then switch to it being true and use the onReadyStateChange.
In my case the error occurred when i tried to access xhr.statusText within the xhr.onreadystatechange method, however retrieving xhr.readyState went just fine.