Domo.js is trying to JSON.parse a png - javascript

While developing a custom app for my organization, I am trying to request the name and the avatar of the individual accessing the card. I am able to get the name of the individual without any problems, but when requesting the avatar image I get the following console error:
Uncaught (in promise) Error: Invalid JSON response at XMLHttpRequest.d.onload (domo.ts:309:18)
I have looked into the domo.js code, and after making some limited sense of things, I found that it tries to JSON.parse the .png that is returned.
When checking the network dev tools tab I can see the correct image getting returned, but it doesn't get passed to the app.
Here is the function that returns the error:
d.onload = function() {
var e;
if( u(d.status) ) {
!["csv","excel"].includes(r.format) && d.response || i(d.response), "blob" === r.responseType && i(new Blob([d.response], { type:d.getResponseHeader("content-type") }));
var t = d.response;
try{
e = JSON.parse(t)
}
catch(e){
return void c(Error("Invalid JSON response"))
}i(e)
}else c(Error(d.statusText))
}
As far as I can tell, e refers to the Domo environment, although I am not 100% sure of that.
Note: I am turning to stackoverflow because my organization still has open support tickets with Domo that are more than 2 years old with no response, so I have little faith in getting a timely response from Domo regarding this issue.
UPDATE: Here is the full function that is called-
function i(e,t,r,n,a) {
return r = r || {}, new Promise((function(i,c) {
var d = new XMLHttpRequest;
if (n?d.open(e,t,n):d.open(e,t), p(d,t,r), function(e,t) {
t.contentType ?
"multipart" !== t.contentType && e.setRequestHeader("Content-Type", t.contentType)
: e.setRequestHeader("Content-Type", o.DataFormats.JSON)
} (d,r), function(e) {
s && e.setRequestHeader("X-DOMO-Ryuu-Token", s)
} (d), function(e,t) {
void 0 !== t.responseType && (e.responseType = t.responseType)
} (d,r),
d.onload = function() {
var e;
if( u(d.status) ) {
!["csv","excel"].includes(r.format) && d.response || i(d.response), "blob" === r.responseType && i(new Blob([d.response], { type:d.getResponseHeader("content-type") }));
var t = d.response;
try{
e = JSON.parse(t)
}
catch(e){
return void c(Error("Invalid JSON response"))
}i(e)
}else c(Error(d.statusText))
},
d.onerror = function() {
c(Error("Network Error"))
}, a)
if (r.contentType && r.contentType !== o.DataFormats.JSON) d.send(a);
else {
var f = JSON.stringify(a);
d.send(f)
}
else d.send()
}))
Here is the domo.js method that is being called to get the image:
e.get = function(e, t) {
return i(o.RequestMethods.GET, e, t)
},

#Skousini you can get the avatar for a user by providing this URL directly to the src property of the <img> tag (obviously replacing the query params with the relevant information):
<img src="/domo/avatars/v2/USER/846578099?size=300&defaultForeground=fff&defaultBackground=000&defaultText=D" />
This documentation is available on developer.domo.com: https://developer.domo.com/docs/dev-studio-references/user-api#User%20Avatar
If you want to pull down data from endpoints, you don't have to use domo.js. You could use axios or any other HTTP tool. domo.js is trying to make HTTP requests simpler by automatically parsing json (since most requests are json based). There are a few other options for what data format that domo.get can support provided in this documentation: https://developer.domo.com/docs/dev-studio-tools/domo-js#domo.get

Related

Service Worker only showing first push notification (from cloud messaging) until I reload - message IS received by the worker

I'm trying to use this Web Push walk-through to allow my customers to get push notifications sent to their phones/desktops: https://framework.realtime.co/demo/web-push/
The demo on the site is working for me, and when I copy it over to my server I'm able to push messages down and I see them being logged in the JavaScript console by my service worker with every push down the channel.
However, only the FIRST message pushed down the channel is causing a notification to appear, the rest simply don't show up. If I revoke the service-worker and reload the page (to get a new one) it works again -- for 1 push.
I'm using the same ortc.js file they are, an almost identical service-worker.js, modified with the ability to pass JSON for image/URL options. My modified service worker code is below.
I'm not getting any errors in the JS console (the 2 in the image above were from something else), but I am getting a red x icon next to the service worker, though the number next to it doesn't seem to be tied to anything I can tell (and clicking it does nothing; clicking the service-worker.js side just drops me to line 1 of the service-worker.js file, below.
My question is: why am I getting the first notification, but not any others? Or how can I go about debugging it? My JS console is showing the payloads, and stepping through the JS with breakpoints has me getting lost in the minified firebase code (I have tried both 3.5 and 6.5 for the firebase.js files).
Here is my service worker:
// Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here, other Firebase libraries
// are not available in the service worker.
importScripts('https://www.gstatic.com/firebasejs/3.5.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.5.0/firebase-messaging.js');
// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
'messagingSenderId': '580405122074'
});
// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
const fb_messaging = firebase.messaging();
// Buffer to save multipart messages
var messagesBuffer = {};
// Gets the number of keys in a dictionary
var countKeys = function (dic) {
var count = 0;
for (var i in dic) {
count++;
}
return count;
};
// Parses the Realtime messages using multipart format
var parseRealtimeMessage = function (message) {
// Multi part
var regexPattern = /^(\w[^_]*)_{1}(\d*)-{1}(\d*)_{1}([\s\S.]*)$/;
var match = regexPattern.exec(message);
var messageId = null;
var messageCurrentPart = 1;
var messageTotalPart = 1;
var lastPart = false;
if (match && match.length > 0) {
if (match[1]) {
messageId = match[1];
}
if (match[2]) {
messageCurrentPart = match[2];
}
if (match[3]) {
messageTotalPart = match[3];
}
if (match[4]) {
message = match[4];
}
}
if (messageId) {
if (!messagesBuffer[messageId]) {
messagesBuffer[messageId] = {};
}
messagesBuffer[messageId][messageCurrentPart] = message;
if (countKeys(messagesBuffer[messageId]) == messageTotalPart) {
lastPart = true;
}
}
else {
lastPart = true;
}
if (lastPart) {
if (messageId) {
message = "";
// Aggregate all parts
for (var i = 1; i <= messageTotalPart; i++) {
message += messagesBuffer[messageId][i];
delete messagesBuffer[messageId][i];
}
delete messagesBuffer[messageId];
}
return message;
} else {
// We don't have yet all parts, we need to wait ...
return null;
}
}
// Shows a notification
function showNotification(message, settings) {
// In this example we are assuming the message is a simple string
// containing the notification text. The target link of the notification
// click is fixed, but in your use case you could send a JSON message with
// a link property and use it in the click_url of the notification
// The notification title
var notificationTitle = 'Web Push Notification';
var title = "Company Name";
var icon = "/img/default.png";
var url = "https://www.example.com/";
var tag = "same";
if(settings != undefined) {
if(hasJsonStructure(settings)) settings = JSON.parse(settings);
title = settings.title;
icon = settings.icon;
url = settings.click_url;
tag = "same";
}
// The notification properties
const notificationOptions = {
body: message,
icon: icon,
data: {
click_url: url
},
tag: tag
};
return self.registration.showNotification(title,
notificationOptions);
}
// If you would like to customize notifications that are received in the
// background (Web app is closed or not in browser focus) then you should
// implement this optional method.
fb_messaging.setBackgroundMessageHandler(function(payload) {
console.log('Received background message ', payload);
// Customize notification here
if(payload.data && payload.data.M) {
var message = parseRealtimeMessage(payload.data.M);
return showNotification(message, payload.data.P);
}
});
// Forces a notification
self.addEventListener('message', function (evt) {
if(hasJsonStructure(evt.data)) {
var opts = JSON.parse(evt.data);
var message = opts.message;
evt.waitUntil(showNotification(message, opts));
}
else evt.waitUntil(showNotification(evt.data));
});
// The user has clicked on the notification ...
self.addEventListener('notificationclick', function(event) {
// Android doesn’t close the notification when you click on it
// See: http://crbug.com/463146
event.notification.close();
if(event.notification.data && event.notification.data.click_url) {
// gets the notitication click url
var click_url = event.notification.data.click_url;
// This looks to see if the current is already open and
// focuses if it is
event.waitUntil(clients.matchAll({
type: "window"
}).then(function(clientList) {
for (var i = 0; i < clientList.length; i++) {
var client = clientList[i];
if (client.url == click_url && 'focus' in client)
return client.focus();
}
if (clients.openWindow) {
var url = click_url;
return clients.openWindow(url);
}
}));
}
});
function hasJsonStructure(str) {
if (typeof str !== 'string') return false;
try {
const result = JSON.parse(str);
const type = Object.prototype.toString.call(result);
return type === '[object Object]'
|| type === '[object Array]';
} catch (err) {
return false;
}
}
I had a similar problem. I was using the tag property from the options object. I gave a fixed value instead of a unique value. So only the first notification was showing up. Then I read this:
tag: An ID for a given notification that allows you to find, replace,
or remove the notification using a script if necessary.
in the documentation and understand cause it needs to be a unique value. So now every notification is showing up. How I see also your tag variable is hardcodated.

Dynamics CRM 2016 Javascript forEach not supported

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.

SignalR update breaks layout and scripts on page

When a SignalR update comes through to our page to update our modal, our item names change and our scripts seem to break.
Brief overview: Our SignalR update gets sent to the website fine, but the data itself has an invalid name.
Once updated, our items refresh with malformed names. Our names shouldn't be updated by SignalR in the first place, and I can't seem to find any references to it in our code.
Closing the modal, our Highcharts and Angular scripts throw console errors.
Server-side code:
public partial class Device
{
if (device != null)
{
if ((Enumerables.DeviceType)device.Type == Enumerables.DeviceType.Store)
SignalrClient.UpdateStore(device.DeviceID);
else // check if need to update a modal on the dashboard
{
foreach (var key in SignalrClient.DevicesDictionary.Keys)
{
var devices = SignalrClient.DevicesDictionary[key];
if (devices != null)
{
if (devices.Contains(device.DeviceID))
SignalrClient.UpdateModal(key, device.DeviceID);
}
}
}
}
}
class SignalrClient
{
public static async Task Start()
{
if (_hubConnection == null || _hubConnection.State == ConnectionState.Disconnected)
{
_hubConnection = new HubConnection("http://stevessiteofamazingboats.net/");
_dashboardHubProxy = _hubConnection.CreateHubProxy("DashboardHub");
_dashboardHubProxy.On("OnRegisterDevice", new Action<string, int>(OnRegisterDevice));
_dashboardHubProxy.On("OnDeregisterDevices", new Action<string>(OnDeregisterDevices));
_dashboardHubProxy.On("OnDeregisterDevice", new Action<string, int>(OnDeregisterDevice));
await _hubConnection.Start();
}
}
public static async void UpdateModal(string connectionId, int deviceId)
{
await Start();
if (_hubConnection.State == ConnectionState.Connected)
await _dashboardHubProxy.Invoke("UpdateModal", new object[] { connectionId, deviceId });
}
}
public class DashboardHub : Hub
{
private static string EventHubConnectionId {get;set;}
private AlarmDBEntities db = Utils.DbContext;
public void UpdateModal(string connectionId, int deviceId)
{
var db = Utils.DbContext;
var device = db.Device.Find(deviceId);
var modal = new Portal.DeviceModalViewModel()
{
DeviceId = deviceId,
SuctionGroups = device.Device1.Where(x => (Enumerables.DeviceType)x.Type == Enumerables.DeviceType.SuctionGroup).Select(x => new DeviceModalViewModel.SGNode()
{
SubChildren = x.Device1.Where(y => (Enumerables.DeviceType)y.Type == Enumerables.DeviceType.Compressor).Select(y => new DeviceModalViewModel.DeviceNode()
{
DeviceId = y.DeviceID,
Name = y.Name,
Amp = db.Property.Where(z => z.Name == "Amps" && z.DeviceID == y.DeviceID).OrderByDescending(z => z.CreatedOn).Select(z => z.Value).FirstOrDefault()
}).OrderBy(y => y.Name).ToList()
}).OrderBy(x => x.Name).ToList(),
};
}
Client-side javascript. The viewModel contains a malformed name:
Viewable on JSFiddle
https://jsfiddle.net/wmqdyv8r/
This is our Angular console error:
angular.min.js:6 Uncaught Error: [ng:areq]
http://errors.angularjs.org/1.5.7/ng/areq?
p0=HeaderController&p1=not%20a%20function%2C%20got%20undefined
at angular.min.js:6
at sb (angular.min.js:22)
at Qa (angular.min.js:23)
at angular.min.js:89
at ag (angular.min.js:72)
at m (angular.min.js:64)
at g (angular.min.js:58)
at g (angular.min.js:58)
at g (angular.min.js:58)
at g (angular.min.js:58)
angular.min.js:312 WARNING: Tried to load angular more than once.
This Highcharts error shows up if we try to open a chart after the SignalR refresh:
store.js:856 Uncaught TypeError: $(...).highcharts is not a function
at Object.success (store.js:856)
at c (<anonymous>:1:132617)
at Object.fireWith [as resolveWith] (<anonymous>:1:133382)
at b (<anonymous>:1:168933)
at XMLHttpRequest.<anonymous> (<anonymous>:1:173769)
Also, after closing the modal, our main page will refresh and now throws this error:
Exception: Sequence contains no elements
Type: System.InvalidOperationException
The main concern is that the update event is breaking something. The naming issue is a lower priority although I'm sure it's related.
Found and fixed the problem!
The malformed name was found to always be trimming the first 5 characters off of the real name, so I fixed that down near the bottom, although I still don't know where this trimming occurs.
The more serious issue, the breaking of scripts was solved as well.
In the UpdateModal method, one of the scripts was looking for a storeID field, along with the deviceID field. After printing a log to the Chrome javascript console, I could see that storeID was always returning 0, even though it was previously initialized before the UpdateModal method.
All I had to do, was follow the damn train add the storeID field seen here under DeviceId:
public void UpdateModal(string connectionId, int deviceId)
{
var db = Utils.DbContext;
var device = db.Device.Find(deviceId);
var modal = new Portal.DeviceModalViewModel()
{
DeviceId = deviceId,
*THIS*-> StoreId = db.Device.Where(x => device.Name.Contains(x.Name.Replace("-Store", "")) && x.ParentID == null).Select(x => x.DeviceID).FirstOrDefault(),
SuctionGroups = device.Device1.Where(x => (Enumerables.DeviceType)x.Type ==
Enumerables.DeviceType.SuctionGroup).Select(x => new
DeviceModalViewModel.SGNode()
{
SubChildren = x.Device1.Where(y => (Enumerables.DeviceType)y.Type ==
Enumerables.DeviceType.Compressor).Select(y => new
DeviceModalViewModel.DeviceNode()
{
DeviceId = y.DeviceID,
*ALSO THIS*-> Name = "12345Comp " + y.Name.Substring(y.Name.Length - 2),
Amp = db.Property.Where(z => z.Name == "Amps" && z.DeviceID == y.DeviceID).OrderByDescending(z => z.CreatedOn).Select(z => z.Value).FirstOrDefault()
}).OrderBy(y => y.Name).ToList()
}).OrderBy(x => x.Name).ToList(),
};

How to retrieve first H1 from an Url?

I am preparing for interview questions from a site , and couldn't think of solution for following, can some body help?
Write a function to retrieve the value of the first H1 element from a given Url.
H1 that contains attributes
H1 that contains nested tags
No H1 found in the HTML
Have a look at the Html agility pack http://htmlagilitypack.codeplex.com/. You can then use linq to answer the question.
So I searched it up online and I found a script and modified it.
I took the file_get_contents function from a project on GitHub.
I hope it will work, tell me, if it's not I will try to fix it.
get.js (var H1 is the first title):
function file_get_contents(url, flags, context, offset, maxLen) {
// discuss at: http://phpjs.org/functions/file_get_contents/
// original by: Legaev Andrey
// input by: Jani Hartikainen
// input by: Raphael (Ao) RUDLER
// improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// improved by: Brett Zamir (http://brett-zamir.me)
// bugfixed by: Brett Zamir (http://brett-zamir.me)
// note: This function uses XmlHttpRequest and cannot retrieve resource from different domain without modifications.
// note: Synchronous by default (as in PHP) so may lock up browser. Can
// note: get async by setting a custom "phpjs.async" property to true and "notification" for an
// note: optional callback (both as context params, with responseText, and other JS-specific
// note: request properties available via 'this'). Note that file_get_contents() will not return the text
// note: in such a case (use this.responseText within the callback). Or, consider using
// note: jQuery's: $('#divId').load('http://url') instead.
// note: The context argument is only implemented for http, and only partially (see below for
// note: "Presently unimplemented HTTP context options"); also the arguments passed to
// note: notification are incomplete
// test: skip
// example 1: var buf file_get_contents('http://google.com');
// example 1: buf.indexOf('Google') !== -1
// returns 1: true
var tmp, headers = [],
newTmp = [],
k = 0,
i = 0,
href = '',
pathPos = -1,
flagNames = 0,
content = null,
http_stream = false;
var func = function(value) {
return value.substring(1) !== '';
};
// BEGIN REDUNDANT
this.php_js = this.php_js || {};
this.php_js.ini = this.php_js.ini || {};
// END REDUNDANT
var ini = this.php_js.ini;
context = context || this.php_js.default_streams_context || null;
if (!flags) {
flags = 0;
}
var OPTS = {
FILE_USE_INCLUDE_PATH: 1,
FILE_TEXT: 32,
FILE_BINARY: 64
};
if (typeof flags === 'number') { // Allow for a single string or an array of string flags
flagNames = flags;
} else {
flags = [].concat(flags);
for (i = 0; i < flags.length; i++) {
if (OPTS[flags[i]]) {
flagNames = flagNames | OPTS[flags[i]];
}
}
}
if (flagNames & OPTS.FILE_BINARY && (flagNames & OPTS.FILE_TEXT)) { // These flags shouldn't be together
throw 'You cannot pass both FILE_BINARY and FILE_TEXT to file_get_contents()';
}
if ((flagNames & OPTS.FILE_USE_INCLUDE_PATH) && ini.include_path && ini.include_path.local_value) {
var slash = ini.include_path.local_value.indexOf('/') !== -1 ? '/' : '\\';
url = ini.include_path.local_value + slash + url;
} else if (!/^(https?|file):/.test(url)) { // Allow references within or below the same directory (should fix to allow other relative references or root reference; could make dependent on parse_url())
href = this.window.location.href;
pathPos = url.indexOf('/') === 0 ? href.indexOf('/', 8) - 1 : href.lastIndexOf('/');
url = href.slice(0, pathPos + 1) + url;
}
var http_options;
if (context) {
http_options = context.stream_options && context.stream_options.http;
http_stream = !! http_options;
}
if (!context || http_stream) {
var req = this.window.ActiveXObject ? new ActiveXObject('Microsoft.XMLHTTP') : new XMLHttpRequest();
if (!req) {
throw new Error('XMLHttpRequest not supported');
}
var method = http_stream ? http_options.method : 'GET';
var async = !! (context && context.stream_params && context.stream_params['phpjs.async']);
if (ini['phpjs.ajaxBypassCache'] && ini['phpjs.ajaxBypassCache'].local_value) {
url += (url.match(/\?/) == null ? '?' : '&') + (new Date())
.getTime(); // Give optional means of forcing bypass of cache
}
req.open(method, url, async);
if (async) {
var notification = context.stream_params.notification;
if (typeof notification === 'function') {
// Fix: make work with req.addEventListener if available: https://developer.mozilla.org/En/Using_XMLHttpRequest
if (0 && req.addEventListener) { // Unimplemented so don't allow to get here
/*
req.addEventListener('progress', updateProgress, false);
req.addEventListener('load', transferComplete, false);
req.addEventListener('error', transferFailed, false);
req.addEventListener('abort', transferCanceled, false);
*/
} else {
req.onreadystatechange = function(aEvt) { // aEvt has stopPropagation(), preventDefault(); see https://developer.mozilla.org/en/NsIDOMEvent
// Other XMLHttpRequest properties: multipart, responseXML, status, statusText, upload, withCredentials
/*
PHP Constants:
STREAM_NOTIFY_RESOLVE 1 A remote address required for this stream has been resolved, or the resolution failed. See severity for an indication of which happened.
STREAM_NOTIFY_CONNECT 2 A connection with an external resource has been established.
STREAM_NOTIFY_AUTH_REQUIRED 3 Additional authorization is required to access the specified resource. Typical issued with severity level of STREAM_NOTIFY_SEVERITY_ERR.
STREAM_NOTIFY_MIME_TYPE_IS 4 The mime-type of resource has been identified, refer to message for a description of the discovered type.
STREAM_NOTIFY_FILE_SIZE_IS 5 The size of the resource has been discovered.
STREAM_NOTIFY_REDIRECTED 6 The external resource has redirected the stream to an alternate location. Refer to message .
STREAM_NOTIFY_PROGRESS 7 Indicates current progress of the stream transfer in bytes_transferred and possibly bytes_max as well.
STREAM_NOTIFY_COMPLETED 8 There is no more data available on the stream.
STREAM_NOTIFY_FAILURE 9 A generic error occurred on the stream, consult message and message_code for details.
STREAM_NOTIFY_AUTH_RESULT 10 Authorization has been completed (with or without success).
STREAM_NOTIFY_SEVERITY_INFO 0 Normal, non-error related, notification.
STREAM_NOTIFY_SEVERITY_WARN 1 Non critical error condition. Processing may continue.
STREAM_NOTIFY_SEVERITY_ERR 2 A critical error occurred. Processing cannot continue.
*/
var objContext = {
responseText: req.responseText,
responseXML: req.responseXML,
status: req.status,
statusText: req.statusText,
readyState: req.readyState,
evt: aEvt
}; // properties are not available in PHP, but offered on notification via 'this' for convenience
// notification args: notification_code, severity, message, message_code, bytes_transferred, bytes_max (all int's except string 'message')
// Need to add message, etc.
var bytes_transferred;
switch (req.readyState) {
case 0:
// UNINITIALIZED open() has not been called yet.
notification.call(objContext, 0, 0, '', 0, 0, 0);
break;
case 1:
// LOADING send() has not been called yet.
notification.call(objContext, 0, 0, '', 0, 0, 0);
break;
case 2:
// LOADED send() has been called, and headers and status are available.
notification.call(objContext, 0, 0, '', 0, 0, 0);
break;
case 3:
// INTERACTIVE Downloading; responseText holds partial data.
bytes_transferred = req.responseText.length * 2; // One character is two bytes
notification.call(objContext, 7, 0, '', 0, bytes_transferred, 0);
break;
case 4:
// COMPLETED The operation is complete.
if (req.status >= 200 && req.status < 400) {
bytes_transferred = req.responseText.length * 2; // One character is two bytes
notification.call(objContext, 8, 0, '', req.status, bytes_transferred, 0);
} else if (req.status === 403) { // Fix: These two are finished except for message
notification.call(objContext, 10, 2, '', req.status, 0, 0);
} else { // Errors
notification.call(objContext, 9, 2, '', req.status, 0, 0);
}
break;
default:
throw 'Unrecognized ready state for file_get_contents()';
}
};
}
}
}
if (http_stream) {
var sendHeaders = http_options.header && http_options.header.split(/\r?\n/);
var userAgentSent = false;
for (i = 0; i < sendHeaders.length; i++) {
var sendHeader = sendHeaders[i];
var breakPos = sendHeader.search(/:\s*/);
var sendHeaderName = sendHeader.substring(0, breakPos);
req.setRequestHeader(sendHeaderName, sendHeader.substring(breakPos + 1));
if (sendHeaderName === 'User-Agent') {
userAgentSent = true;
}
}
if (!userAgentSent) {
var user_agent = http_options.user_agent || (ini.user_agent && ini.user_agent.local_value);
if (user_agent) {
req.setRequestHeader('User-Agent', user_agent);
}
}
content = http_options.content || null;
/*
// Presently unimplemented HTTP context options
var request_fulluri = http_options.request_fulluri || false; // When set to TRUE, the entire URI will be used when constructing the request. (i.e. GET http://www.example.com/path/to/file.html HTTP/1.0). While this is a non-standard request format, some proxy servers require it.
var max_redirects = http_options.max_redirects || 20; // The max number of redirects to follow. Value 1 or less means that no redirects are followed.
var protocol_version = http_options.protocol_version || 1.0; // HTTP protocol version
var timeout = http_options.timeout || (ini.default_socket_timeout && ini.default_socket_timeout.local_value); // Read timeout in seconds, specified by a float
var ignore_errors = http_options.ignore_errors || false; // Fetch the content even on failure status codes.
*/
}
if (flagNames & OPTS.FILE_TEXT) { // Overrides how encoding is treated (regardless of what is returned from the server)
var content_type = 'text/html';
if (http_options && http_options['phpjs.override']) { // Fix: Could allow for non-HTTP as well
content_type = http_options['phpjs.override']; // We use this, e.g., in gettext-related functions if character set
// overridden earlier by bind_textdomain_codeset()
} else {
var encoding = (ini['unicode.stream_encoding'] && ini['unicode.stream_encoding'].local_value) ||
'UTF-8';
if (http_options && http_options.header && (/^content-type:/im)
.test(http_options.header)) { // We'll assume a content-type expects its own specified encoding if present
content_type = http_options.header.match(/^content-type:\s*(.*)$/im)[1]; // We let any header encoding stand
}
if (!(/;\s*charset=/)
.test(content_type)) { // If no encoding
content_type += '; charset=' + encoding;
}
}
req.overrideMimeType(content_type);
}
// Default is FILE_BINARY, but for binary, we apparently deviate from PHP in requiring the flag, since many if not
// most people will also want a way to have it be auto-converted into native JavaScript text instead
else if (flagNames & OPTS.FILE_BINARY) { // Trick at https://developer.mozilla.org/En/Using_XMLHttpRequest to get binary
req.overrideMimeType('text/plain; charset=x-user-defined');
// Getting an individual byte then requires:
// responseText.charCodeAt(x) & 0xFF; // throw away high-order byte (f7) where x is 0 to responseText.length-1 (see notes in our substr())
}
try {
if (http_options && http_options['phpjs.sendAsBinary']) { // For content sent in a POST or PUT request (use with file_put_contents()?)
req.sendAsBinary(content); // In Firefox, only available FF3+
} else {
req.send(content);
}
} catch (e) {
// catches exception reported in issue #66
return false;
}
tmp = req.getAllResponseHeaders();
if (tmp) {
tmp = tmp.split('\n');
for (k = 0; k < tmp.length; k++) {
if (func(tmp[k])) {
newTmp.push(tmp[k]);
}
}
tmp = newTmp;
for (i = 0; i < tmp.length; i++) {
headers[i] = tmp[i];
}
this.$http_response_header = headers; // see http://php.net/manual/en/reserved.variables.httpresponseheader.php
}
if (offset || maxLen) {
if (maxLen) {
return req.responseText.substr(offset || 0, maxLen);
}
return req.responseText.substr(offset);
}
return req.responseText;
}
return false;
}
var filecontent = file_get_contents("www.example.com");
var H1 = $( "h1" ).first().val();
if you would do it on the client side using jquery you could do it like this
$('#mydiv').load("http://localhost/mypage.html");
var firstH1 = $('#mydiv').children('h1:first-child');
console.log(firstH1);

Create Firefox Addon to Watch and modify XHR requests & reponses

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.

Categories

Resources