I'm getting the following error when attempting to get an enumerator for a collection of lists: "Uncaught Error: The collection has not been initialized. It has not been requested or the request has not been executed. It may need to be explicitly requested."
It happens on the line var listEnumerator = lists.getEnumerator(); it seems to me that there is an issue in my attempt to load lists into the client object with context.load(lists);
Here's the portion of my code that's causing the problem. I've marked the place just before the error is thrown.
//____________________________Required function for accessing the host site's info.___________________________________
function getQueryStringParameter(param) {
var params = document.URL.split("?")[1].split("&");
for (var i = 0; i < params.length; i = i + 1) {
var singleParam = params[i].split("=");
if (singleParam[0] == param) {
return singleParam[1];
}
}
}
//____________________________Begin checking for list_________________________
function checkForList(listToFind, typeOfListToCreateIfTheListIsMissing)
{
var hostUrl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
var hostcontext = new SP.AppContextSite(context, hostUrl);
var hostweb = hostcontext.get_web();
var lists = hostweb.get_lists();
context.load(lists);
context.executeQueryAsync(checkIfListExistsUsingEnumerator(listToFind, lists, hostweb, typeOfListToCreateIfTheListIsMissing), onQueryFailed);
}
//Failed to get lists for some reason
function onQueryFailed(sender, args) {
alert('We failed to retrieve lists. \n' + args.get_message() + '\n' + args.get_stackTrace());
}
//____________________________Does list exist?____________________________
function checkIfListExistsUsingEnumerator(listToFind, lists, hostweb, typeOfList)
{
var listExists = false;
//!!!!!!!!!!!!!!! ERROR HERE !!!!!!!!!!!!!!!!
var listEnumerator = lists.getEnumerator();
var title;
while (listEnumerator.moveNext())
{
title = listEnumerator.get_current().get_title();
if (title == listToFind)
{
listExists = true;
}
}
if (!listExists)
{
alert("It appears that a required list does not already exist. \nClick ok, and we'll automatically create one for you.");
//Create a new list
createList(listToFind, hostweb, typeOfList);
}
else if (listExists)
{
//Do nothing.
}
}
//____________________________If it doesn't, create one on the local site____________________________
function createList(nameOfNewList, hostweb, typeOfList) {
var listCreationInfo = new SP.ListCreationInformation();
listCreationInfo.set_title(nameOfNewList);
if (typeOfList === "events")
{
listCreationInfo.set_templateType(SP.ListTemplateType.events);
}
else if (typeOfList === "contacts")
{
listCreationInfo.set_templateType(SP.ListTemplateType.contacts);
}
var lists = hostweb.get_lists();
var newList = lists.add(listCreationInfo);
context.load(newList);
context.executeQueryAsync(onListCreationSuccess, onListCreationFail);
}
function onListCreationSuccess() {
alert('List created successfully!');
}
function onListCreationFail(sender, args) {
alert('Failed to create the list. ' + args.get_message());
}
I've looked at this question sharepoint javascript collection not initialized error which seems to be fairly similar to mine, but I'm having trouble implementing the solution provided there, making me think my error may be have a different cause.
I've also tried querying for the lists inside of the function that is throwing the error, but that doesn't seem to solve anything.
For a little background, these functions are attempting to read all lists from the app's host site, check to see if a specified list exists, and create a list if no matching list exists. If there's a better way of doing that than what I'm attempting, I'd be open to that too.
Any pointers?
Some things I've tried that don't seem to work:
Changing the Asynchronous query
context.executeQueryAsync(checkIfListExists(listToFind, hostweb, typeOfListToCreateIfTheListIsMissing), onQueryFailed);
to a Synchronous one.
context.executeQuery(checkIfListExists(listToFind, hostweb, typeOfListToCreateIfTheListIsMissing), onQueryFailed);
I've figured out an alternate, and shorter way to method of achieving the same goal I was trying to achieve before.
Instead of checking to see if a list does not already exist, I just try to create a list, and the Query fails to create a list if one is already there. (That's good because I don't want to overwrite the list if it is already there.)
I'm not totally sure if there are any undesired side effects of what I'm doing here, but in my tests it produced the desired behavior.
//____________________________Required function for accessing the host site's info.___________________________________
function getQueryStringParameter(param) {
var params = document.URL.split("?")[1].split("&");
for (var i = 0; i < params.length; i = i + 1) {
var singleParam = params[i].split("=");
if (singleParam[0] == param) {
return singleParam[1];
}
}
}
//____________________________Create a list if one does not already exist_________________________
function createList(listToCreate, typeOfList)
{
// Create an announcement SharePoint list with the name that the user specifies.
var hostUrl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
var hostContext = new SP.AppContextSite(currentContext, hostUrl);
var hostweb = hostContext.get_web();
var listCreationInfo = new SP.ListCreationInformation();
listCreationInfo.set_title(listToCreate);
if (typeOfList === "events")
{
listCreationInfo.set_templateType(SP.ListTemplateType.events);
}
else if (typeOfList === "contacts")
{
listCreationInfo.set_templateType(SP.ListTemplateType.contacts);
}
var lists = hostweb.get_lists();
var newList = lists.add(listCreationInfo);
currentContext.load(newList);
currentContext.executeQueryAsync(onListCreationSuccess, onListCreationFail);
}
function onListCreationSuccess() {
alert("We've created a list since one doesn't exist yet." );
}
function onListCreationFail(sender, args) {
alert("We didn't create the list. Here's why: " + args.get_message());
}
Related
I'm trying to change the color of elements in 3D Viewer using the Autodesk-forge platform, and for this I'm using this API https://forge.autodesk.com/cloud_and_mobile/2015/12/change-color-of-elements-with-view-and-data-api.html by Daniel Du.
But the problem is when running I got this
The error Pict
And this the function :
Autodesk.Viewing.Viewer3D.prototype.setColorMaterial = function(objectIds, color) {
var material = addMaterial(color);
for (var i=0; i<objectIds.length; i++) {
var dbid = objectIds[i];
//from dbid to node, to fragid
viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, function () {
var it = viewer.model.getData().instanceTree;
console.log(it);
it.enumNodeFragments(dbid, function (fragId) {
var renderProxy = viewer.impl.getRenderProxy(viewer.model, fragId);
console.log("r prox : " + renderProxy);
renderProxy.meshProxy = new THREE.Mesh(renderProxy.geometry, renderProxy.material);
renderProxy.meshProxy.matrix.copy(renderProxy.matrixWorld);
renderProxy.meshProxy.matrixWorldNeedsUpdate = true;
renderProxy.meshProxy.matrixAutoUpdate = false;
renderProxy.meshProxy.frustumCulled = false;
viewer.impl.addOverlay(overlayName, renderProxy.meshProxy);
viewer.impl.invalidate(true);
}, false);
});
}
}
Hopefully, anyone has the solution to this problem...
Most likely you are running this code before the instance tree has been loaded, which provokes the error Cannot read property 'enumNodeFragments' of undefined on it variable. You would need to wait for the Autodesk.Viewing.OBJECT_TREE_CREATED_EVENT before running that code.
Take also a look at previous question about modifying materials in the viewer.
So I am trying to write javascript code for a ribbon button in Dynamics CRM 2016 that will grab a phone number from a list of Leads that can be seen in the Active Leads window.
However, when I try to run it, I get an error telling me
As I step into my code (I'm debugging), I see this error
Here is the code I am working with.
function updateSelected(SelectedControlSelectedItemIds, SelectedEntityTypeName) {
// this should iterate through the list
SelectedControlSelectedItemIds.forEach(
function (selected, index) {
//this should get the id and name of the selected lead
getPhoneNumber(selected, SelectedEntityTypeName);
});
}
//I should have the lead ID and Name here, but it is returning null
function getPhoneNumber(id, entityName) {
var query = "telephone1";
Sdk.WebApi.retrieveRecord(id, entityName, query, "",
function (result) {
var telephone1 = result.telephone1;
// I'm trying to capture the number and display it via alert.
alert(telephone1);
},
function (error) {
alert(error);
})
}
Any help is appreciated.
What you have is an javascript error. In js you can only use forEach on an array. SelectedControlSelectedItemIds is an object not an array.
To loop though an object, you can do the following.
for (var key in SelectedControlSelectedItemIds){
if(SelectedControlSelectedItemIds.hasOwnProperty(key)){
getPhoneNumber(SelectedControlSelectedItemIds[key], SelectedEntityTypeName)
}
}
Okay, so I figured it out. I had help, so I refuse to take full credit.
First, I had to download the SDK.WEBAPI.
I then had to add the webAPI to my Javascript Actions in the Ribbon Tool Bench.
Then, I had to create a function to remove the brackets around the
SelectedControlSelectedItemIds
Firstly, I had to use the API WITH the forEach method in order for it to work.
These are the revisions to my code.
function removeBraces(str) {
str = str.replace(/[{}]/g, "");
return str;
}
function updateSelected(SelectedControlSelectedItemIds, SelectedEntityTypeName) {
//alert(SelectedEntityTypeName);
SelectedControlSelectedItemIds.forEach(
function (selected, index) {
getPhoneNumber(removeBraces(selected), SelectedEntityTypeName);
// alert(selected);
});
}
function getPhoneNumber(id, entityName) {
var query = "telephone1";
SDK.WEBAPI.retrieveRecord(id, entityName, query, "",
function (result) {
var telephone1 = result.telephone1;
formatted = telephone1.replace(/[- )(]/g,'');
dialready = "1" + formatted;
withcolon = dialready.replace(/(.{1})/g,"$1:")
number = telephone1;
if (Xrm.Page.context.getUserName() == "Jerry Ryback") {
url = "http://111.222.333.444/cgi-bin/api-send_key";
} else if(Xrm.Page.context.getUserName() == "Frank Jane") {
url = "http://222.333.444.555/cgi-bin/api-send_key";
}
else if( Xrm.Page.context.getUserName() == "Bob Gilfred"){
url = "http://333.444.555.666/cgi-bin/api-send_key";
}
else if( Xrm.Page.context.getUserName() == "Cheryl Bradley"){
url = "http://444.555.666.777/cgi-bin/api-send_key";
}
else if( Xrm.Page.context.getUserName() == "Bill Dunny"){
url = "http://555.666.777.888/cgi-bin/api-send_key";
}
if (url != "") {
var params = "passcode=admin&keys=" + withcolon + "SEND";
var http = new XMLHttpRequest();
http.open("GET", url + "?" + params, true);
http.onreadystatechange = function () {
if (http.readyState == 4 && http.status == 200) {
alert(http.responseText);
}
}
http.send(null);
}
},
function (error) {
// alert(error);
})
}
To elaborate, once I successfully get the number, I remove the parenthesis, dashes and white-space. Then, I add a "1" to the beginning. Finally, I insert colons in between each number. Then, I create an HTTP command and send it to the office phone of whoever is using CRM at the time. The user eval and HTTP message is my code. I'm showing you all of this because it was a great learning experience, and this feature really adds to the functionality.
I hope some of you find this useful.
Thanks for the help.
I am still struggling with getting a chain of deferred promises working on a Visual Web Part. The end goal is to change the colour of some DIV tags to colours referenced in a SPList.
I originally had this code working with a single request to lookup data from a list, but I am now looking for a chain of requests. The first is finding name of the Sub Site from the window.location.href property. Although this code is retrieving the sub site name thinking about this as if type this might be my first mistake and perhaps I should be getting this from the SPWeb object instead. The second then uses this value in a where clause to retrieve the correct data from the second call to a different list.
So the code if have at the moment works, but is intermittent. I think I'm not calling .resolve() in the correct place, and it is just luck as to if the async code has completed before the calling thread uses the expected result. It is therefore in the lap of the processor gods and probably works 50:50. This at least proves my jQuery code is producing the desired result.
I have two functions doing almost exactly the same thing to two different DIV elements. This is what I have at the moment.
function alterMenuColour(id) {
getMenuItemfromURLValue(window.location.href).done(function (urlSelection) {
var colkey = 0; //this is the key for the list collection array. This needs to be uneque for each different call to retrieveListItems.
var promise = retrieveListItems('/sites/OMGIntranet/OMGCentral/', 'MenuItemList', '<View><Query><Where><Eq><FieldRef Name=\'MenuItem\'/><Value Type=\'Text\'>' + urlSelection + '</Value></Eq></Where></Query><RowLimit>1</RowLimit></View>', 'Id,MenuColour,BarColour', colkey);
var collMenuListItem = collListItem[colkey];
promise.done(
function (collMenuListItem) {
var listItemEnumerator = collMenuListItem.getEnumerator();
var oListItem;
while (listItemEnumerator.moveNext()) {
oListItem = listItemEnumerator.get_current();
}
var menus = getChildElementsByType(document, id, 'div');
jQuery(menus).children("div").each(function () {
jQuery(this).css("background", oListItem.get_item('MenuColour'));
});
},
function (sender, args) {
alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
);
});
}
function alterBarColour() {
getMenuItemfromURLValue(window.location.href).done(function (urlSelection) {
var colkey = 1; //this is the key for the list collection array. This needs to be uneque for each different call to retrieveListItems.
var barpromise = retrieveListItems('/sites/OMGIntranet/OMGCentral/', 'MenuItemList', '<View><Query><Where><Eq><FieldRef Name=\'MenuItem\'/><Value Type=\'Text\'>' + urlSelection + '</Value></Eq></Where></Query><RowLimit>1</RowLimit></View>', 'Id,MenuColour,BarColour', colkey);
var collBarListItem = collListItem[colkey];
barpromise.done(
function (collBarListItem) {
var listItemEnumerator = collBarListItem.getEnumerator();
var oListItem;
while (listItemEnumerator.moveNext()) {
oListItem = listItemEnumerator.get_current();
}
var bar = document.getElementsByClassName('SectionMenuBar');
jQuery(bar).each(function () {
jQuery(this).css("background", oListItem.get_item('BarColour'));
});
},
function (sender, args) {
alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
);
});
}
I have created a function getMenuItemfromURLValue(url) which is the method if described earlier to get the Sub Site name.
function getMenuItemfromURLValue(url) {
var colkey = 2; //this is the key for the list collection array. This needs to be uneque for each different call to retrieveListItems.
var promise = retrieveListItems('/sites/OMGIntranet/OMGCentral/', 'SectionMenuAssignmentList', '<View><Query><OrderBy><FieldRef Name=\'ID\'/></OrderBy></Query></View>', 'MenuItemLookup', colkey);
var collSelectionMenuAssignemntListItem = collListItem[colkey];
return promise.then(
function (collSelectionMenuAssignemntListItem) {
var listItemEnumerator = collSelectionMenuAssignemntListItem.getEnumerator();
var oListItem;
while (listItemEnumerator.moveNext()) {
oListItem = listItemEnumerator.get_current();
if (isStringMatch(decodeURI(url), oListItem.get_item('MenuItemLookup').$2d_1)) {
return oListItem.get_item('MenuItemLookup').$2d_1;
}
}
},
function (sender, args) {
alert('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
}
);
}
I am trying to write reusable code but it strikes me that it makes it harder to comprehend when using the deferred methods like .then()
I have a function to get the list data from SharePoint SPList which again on the same theme is reusable:
//http://www.shillier.com/archive/2013/03/04/using-promises-with-the-javascript-client-object-model-in-sharepoint-2013.aspx
function retrieveListItems(siteUrl, list, calm, include, collkey) {
var deferred = $.Deferred();
var clientContext
if (siteUrl == null) {
clientContext = sharePointCurrentClientContext();
} else {
clientContext = new SP.ClientContext(siteUrl);
}
var oList = clientContext.get_web().get_lists().getByTitle(list);
var camlQuery = new SP.CamlQuery();
camlQuery.set_viewXml(calm);
if (typeof this.collListItem === 'undefined') {
this.collListItem = [];
}
this.collListItem.add(collkey, oList.getItems(camlQuery));
if (include == null) {
clientContext.load(collListItem[collkey]);
} else {
clientContext.load(collListItem[collkey], 'Include(' + include + ')');
}
clientContext.executeQueryAsync(Function.createDelegate(this, function () { deferred.resolve(collListItem[collkey]) }), Function.createDelegate(this, function (sender, args) { deferred.reject(sender, args); }));
return deferred.promise();
}
Originally when I wrote this function the only way if could figure out how to return data successfully was in a global list collection object called collListItem. This is horrible I hate this and have been trying to remove it but so far unsuccessfully. It does work though, and having read and read and read, some people suggest that objects cannot be returned via deferred methods and that they have to be chained to pass on data. This again brings me back to being able to have reusable code.
I have got it working 100% of the time now, which is the goal, but I'm still not happy with it it doesn't seem to be elegant code and I'm still strugling with the concept.
I have changed,
function alterMenuColour(id) {
- getMenuItemfromURLValue(window.location.href).done(function (urlSelection) {
+ getMenuItemfromURLValue(window.location.href, 3).done(function (urlSelection) {
and
function alterBarColour() {
- getMenuItemfromURLValue(window.location.href).done(function (urlSelection) {
+ getMenuItemfromURLValue(window.location.href, 4).done(function (urlSelection) {
and
-function getMenuItemfromURLValue(url) {
-
- var colkey = 2; //this is the key for the list collection array. This needs to be uneque for each different call to retrieveListItems.
+function getMenuItemfromURLValue(url, colkey) {
I got a main process and this fork a child. This child do some calculations. At some point of the code in the child I want to ask the parent for some data. This request is highly dynamic and at this point i want to wait for a answer/response of this request. But how do I wait for process.send() or is it possible to add a Callback function to .send()?
I tried to break down my Problem to a simple example.
The highly dynamic value is in my example the randomval in the worker.
And i know that the assignment
var c = process.send({msg:'get_c',randomval:Math.floor((Math.random()*10)+1)});
can't work. But i no other idea how to describe the Problem.
main.js
var childProcessCalc = require('child_process').fork(__dirname + '/worker');
childProcessCalc.send({msgtype:'docalc'});
childProcessCalc.on('message',function(msg){
if(msg.msg === 'pi')
{
console.log("Pi"+msg.pi+" by c="+msg.c);
}
else if(msg.msg === 'get_c')
{
console.log('child wants c');
childProcessCalc.send({msgtype:'your_c',c:1000000*msg.randomval});
}
});
childProcessCalc.on('exit',function(){
console.log('main:the childProzess has exit!')
});
worker.js
process.on('message', function(msg){
if(msg.msgtype == 'docalc') {
//Here is my Problem, how to wait for the message thats the response for
//exactly this send / randomval, or how to add a callback to send
var c = process.send({msg:'get_c',randomval:Math.floor((Math.random()*10)+1)});
var Pi=0;
var n=1;
for (var i=0;i<=c;i++)
{
Pi=Pi+(4/n)-(4/(n+2))
n=n+4
}
process.send({msg:'pi',pi:Pi,c:c})
}
else if(msg.msgtype === 'your_c')
{
console.log('parent hase sendc='+msg.c);
}
});
I have a solution to my problem and it works well for me, but because im very new at nodejs i still not now if this is the best way. Its feel like a overhead.
In a few words what i have done:
I added a object that stores a random callback identifier with the callback function that has to be called if we got a response for the given callback identifier.
When i now call send() from worker i send the identifier to the main process and the main process send this identifier back when he has finished. So i can lookup in my callback var (dynamicMassages ) for the callbackfnc to call and execute it.
main2.js
var childProcessCalc = require('child_process').fork(__dirname + '/worker2');
childProcessCalc.send({msgtype:'docalc'});
childProcessCalc.send({msgtype:'docalc'});
childProcessCalc.on('message',function(msg){
if(msg.msg === 'pi')
{
console.log("Pi"+msg.pi+" by c="+msg.c);
}
else if(msg.msg === 'get_c')
{
console.log('child wants c');
childProcessCalc.send({msgtype:'your_c',callbackid:msg.callbackid, c:1000000*msg.randomval});
}
});
childProcessCalc.on('exit',function(){
console.log('main:the childProzess has exit!')
});
worker2.js
var dynamicMassages = {};
process.on('message', function(msg){
var getRandomId = function(){
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for( var i=0; i < 5; i++ )
{
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
if(dynamicMassages[text] === undefined)
{
return text;
}
else
{
return getRandomId();
}
};
if(msg.msgtype == 'docalc') {
var randomId = getRandomId();
var callbackFnc = function(c){
var Pi=0;
var n=1;
for (var i=0;i<=c;i++)
{
Pi=Pi+(4/n)-(4/(n+2))
n=n+4
}
console.log("callbackFnc For:"+randomId);
process.send({msg:'pi',pi:Pi,c:c})
delete dynamicMassages[randomId];
};
dynamicMassages[randomId] = callbackFnc;//callbackFnc;
process.send({msg:'get_c',callbackid: randomId, randomval:Math.floor((Math.random()*10)+1)});
}
else if(msg.msgtype === 'your_c')
{
console.log('parent hase sendc='+msg.c+' for callbackId '+msg.callbackid);
if(msg.callbackid !== undefined)
{
dynamicMassages[msg.callbackid](msg.c);
}
}
});
Please leave a comment if you would to it the same way.
I'd suggest that you go with a message bus, either a full-blown advanced solution such as RabbitMQ, or a little smaller solution, such as axon.
Basically, what you want to do is inter-process communication, and I'd try to stick to established protocols and standards as much as possible, and avoid rolling your very own solution. As RabbitMQ builds on top of AMQP, I guess you can call it standard.
Update: I guess the subject gave a wrong notion that I'm looking for an existing addon. This is a custom problem and I do NOT want an existing solution.
I wish to WRITE (or more appropriately, modify and existing) Addon.
Here's my requirement:
I want my addon to work for a particular site only
The data on the pages are encoded using a 2 way hash
A good deal of info is loaded by XHR requests, and sometimes
displayed in animated bubbles etc.
The current version of my addon parses the page via XPath
expressions, decodes the data, and replaces them
The issue comes in with those bubblified boxes that are displayed
on mouse-over event
Thus, I realized that it might be a good idea to create an XHR
bridge that could listen to all the data and decode/encode on the fly
After a couple of searches, I came across nsITraceableInterface[1][2][3]
Just wanted to know if I am on the correct path. If "yes", then kindly
provide any extra pointers and suggestions that may be appropriate;
and if "No", then.. well, please help with correct pointers :)
Thanks,
Bipin.
[1]. https://developer.mozilla.org/en/NsITraceableChannel
[2]. http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/
[3]. http://www.ashita.org/howto-xhr-listening-by-a-firefox-addon/
nsITraceableChannel is indeed the way to go here. the blog posts by Jan Odvarko (softwareishard.com) and myself (ashita.org) show how to do this. You may also want to see http://www.ashita.org/implementing-an-xpcom-firefox-interface-and-creating-observers/, however it isn't really necessary to do this in an XPCOM component.
The steps are basically:
Create Object prototype implementing nsITraceableChannel; and create observer to listen to http-on-modify-request and http-on-examine-response
register observer
observer listening to the two request types adds our nsITraceableChannel object into the chain of listeners and make sure that our nsITC knows who is next in the chain
nsITC object provides three callbacks and each will be called at the appropriate stage: onStartRequest, onDataAvailable, and onStopRequest
in each of the callbacks above, our nsITC object must pass on the data to the next item in the chain
Below is actual code from a site-specific add-on I wrote that behaves very similarly to yours from what I can tell.
function TracingListener() {
//this.receivedData = [];
}
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);
//var data = inputStream.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.receivedData = [];
this.originalListener.onStartRequest(request, context);
},
onStopRequest: function(request, context, statusCode)
{
try
{
request.QueryInterface(Ci.nsIHttpChannel);
if (request.originalURI && piratequesting.baseURL == request.originalURI.prePath && request.originalURI.path.indexOf("/index.php?ajax=") == 0)
{
var data = null;
if (request.requestMethod.toLowerCase() == "post")
{
var postText = this.readPostTextFromRequest(request, context);
if (postText)
data = ((String)(postText)).parseQuery();
}
var date = Date.parse(request.getResponseHeader("Date"));
var responseSource = this.receivedData.join('');
//fix leading spaces bug
responseSource = responseSource.replace(/^\s+(\S[\s\S]+)/, "$1");
piratequesting.ProcessRawResponse(request.originalURI.spec, responseSource, date, data);
}
}
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;
},
readPostTextFromRequest : function(request, context) {
try
{
var is = request.QueryInterface(Ci.nsIUploadChannel).uploadStream;
if (is)
{
var ss = is.QueryInterface(Ci.nsISeekableStream);
var prevOffset;
if (ss)
{
prevOffset = ss.tell();
ss.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
}
// Read data from the stream..
var charset = "UTF-8";
var text = this.readFromStream(is, charset, true);
// Seek locks the file so, seek to the beginning only if necko hasn't read it yet,
// since necko doesn't seek to 0 before reading (at lest not till 459384 is fixed).
if (ss && prevOffset == 0)
ss.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
return text;
}
else {
dump("Failed to Query Interface for upload stream.\n");
}
}
catch(exc)
{
dumpError(exc);
}
return null;
},
readFromStream : function(stream, charset, noClose) {
var sis = CCSV("#mozilla.org/binaryinputstream;1", "nsIBinaryInputStream");
sis.setInputStream(stream);
var segments = [];
for (var count = stream.available(); count; count = stream.available())
segments.push(sis.readBytes(count));
if (!noClose)
sis.close();
var text = segments.join("");
return text;
}
}
hRO = {
observe: function(request, aTopic, aData){
try {
if (typeof Cc == "undefined") {
var Cc = Components.classes;
}
if (typeof Ci == "undefined") {
var Ci = Components.interfaces;
}
if (aTopic == "http-on-examine-response") {
request.QueryInterface(Ci.nsIHttpChannel);
if (request.originalURI && piratequesting.baseURL == request.originalURI.prePath && request.originalURI.path.indexOf("/index.php?ajax=") == 0) {
var newListener = new TracingListener();
request.QueryInterface(Ci.nsITraceableChannel);
newListener.originalListener = request.setNewListener(newListener);
}
}
} catch (e) {
dump("\nhRO error: \n\tMessage: " + e.message + "\n\tFile: " + e.fileName + " line: " + e.lineNumber + "\n");
}
},
QueryInterface: function(aIID){
if (typeof Cc == "undefined") {
var Cc = Components.classes;
}
if (typeof Ci == "undefined") {
var Ci = Components.interfaces;
}
if (aIID.equals(Ci.nsIObserver) ||
aIID.equals(Ci.nsISupports)) {
return this;
}
throw Components.results.NS_NOINTERFACE;
},
};
var observerService = Cc["#mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
observerService.addObserver(hRO,
"http-on-examine-response", false);
In the above code, originalListener is the listener we are inserting ourselves before in the chain. It is vital that you keep that info when creating the Tracing Listener and pass on the data in all three callbacks. Otherwise nothing will work (pages won't even load. Firefox itself is last in the chain).
Note: there are some functions called in the code above which are part of the piratequesting add-on, e.g.: parseQuery() and dumpError()
Tamper Data Add-on. See also the How to Use it page
You could try making a Greasemonkey script and overwriting the XMLHttpRequest.
The code would look something like:
function request () {
};
request.prototype.open = function (type, path, block) {
GM_xmlhttpRequest({
method: type,
url: path,
onload: function (response) {
// some code here
}
});
};
unsafeWindow.XMLHttpRequest = request;
Also note that you can turn a GM script into an addon for Firefox.