Chrome Extension to Modify request headers based on Public IP - javascript

I am trying to create a small Chrome Extension to add a header when a user is on one of our Public IP address. I am relatively new to javascript, and could use a little help.
Here is the function to get the public IP address:
function getIP(){
$.getJSON("http://ip-api.com/json",
function(data){
if(data.query == 'xxx.x.xxx.xxx'){var indist = 1;}else{var indist = 0;}
console.log(indist);
return(indist);
});
}
When I retrieve the Pubic IP
var indist = getIP();
Then the modification of headers in the Chrome Extension.
chrome.webRequest.onBeforeSendHeaders.addListener(
function(details) {
if(indist === 1){
details.requestHeaders.push({name:"X-YouTube-Edu-Filter",value:"xxxxxxxxxxxxx"});
return {requestHeaders: details.requestHeaders};}
},{urls: ["<all_urls>"]},["requestHeaders", "blocking"]
);
This is my first shot at it, and I am not getting the results, I would like. Ideally, when a user makes a request for anything on Youtube, I would like to check the users Public IP Address once, then add the header based on that address.
Any help would be greatly appreciated!

Your function getIP does not actually return anything, because $.getJSON is asynchronous.
Your code looks like this:
function getIP(){
$.getJSON("http://ip-api.com/json", someFunction);
}
And this reads as "queue up the request, and when it's finished call someFunction". Then the statement terminates, and your function getIP ends without returning anything.
The value you return in your callback is, therefore, lost.
Additionally, your scoping is wrong:
if(data.query == 'xxx.x.xxx.xxx') {
/* scope begins */
var indist = 1; // Local, only exists within this branch!
/* scope ends */
} else {
var indist = 0;
}
console.log(indist); // Is not related to above, probably undefined
As a simple fix, since you're using a global variable anyway in your onBeforeSendHeaders handler, just assign it inside the callback:
function getIP(){
$.getJSON(
"http://ip-api.com/json",
function(data) {
if(data.query == 'xxx.x.xxx.xxx') {
indist = 1; // GLOBAL variable
} else {
indist = 0;
}
console.log(indist);
}
);
}
That said, you probably want to limit the damage your code does. Running blocking code on EVERY SINGLE network request will slow Chrome to a crawl. Rewrite your code to only enable the handler if the condition is met, and then probably only run it on YouTube requests.
function getIP(){
$.getJSON(
"http://ip-api.com/json",
function(data) {
chrome.webRequest.onBeforeSendHeaders.removeListener(addHeader);
if(data.query == 'xxx.x.xxx.xxx') {
chrome.webRequest.onBeforeSendHeaders.addListener(
addHeader,
{urls: ["*://*.youtube.com/*"]},
["requestHeaders", "blocking"]
);
}
}
);
}
function addHeader(details) {
details.requestHeaders.push({name:"X-YouTube-Edu-Filter",value:"xxxxxxxxxxxxx"});
return {requestHeaders: details.requestHeaders};
}

Related

How can I detect that new content has been loaded in response to a scroll trigger?

I'm running a script on Facebook that requires me to get the IDs of people in my "friends" window (this might not be the most efficient way to accomplish this specific task, but since I'd like to know how to do this in general it's a good example).
This means that if I have more than a small number of friends I have to scroll down for Facebook to add them to the page.
I've added logic that scrolls the page down to the footer, but I don't know how to force my function that grabs the IDs to run after the content loads.
For now, I've resorted to using setTimeout for a few seconds - obviously, this isn't guaranteed to at the appropriate time, so I'd like to know how to do this properly:
var k;
function doit(){
k = document.getElementsByClassName("_698");
var g= Array.prototype.slice.call(k);
confirm(g.length);
// the confirm is just to make sure it's working
// (if i don't use setTimeout it'll return a smaller number
// since not all the friends were included)
}
window.addEventListener("load", function(){
document.getElementById( "pageFooter" )
.scrollIntoView();setTimeout(doit,3000);
});
Crayon Violent details how to accomplish this in his answer to JavaScript detect an AJAX event. The trick is to hook the underlying XMLHttpRequest object in order to detect when a request is sent.
I've re-written the logic there a bit to make it more suitable for your needs:
//
// Hooks XMLHttpRequest to log all AJAX requests.
// Override ajaxHook.requestCompleted() to do something specific
// in response to a given request.
//
var ajaxHook = (function()
{
// we're using a self-executing function here to avoid polluting the global
// namespace. The hook object is returned to expose just the properties
// needed by client code.
var hook = {
// by default, just logs all requests to the console.
// Can be overridden to do something more interesting.
requestCompleted: function(xmlHttp, url, method) { console.log(url); }
};
// hook open() to store URL and method
var oldOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url)
{
this.hook_method = method;
this.hook_url = url;
oldOpen.apply(this, arguments);
}
// hook send() to allow hooking onreadystatechange
var oldSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function()
{
var xmlhttp = this;
//hook onreadystatechange event to allow processing results
var oldReadyStateChange = xmlhttp.onreadystatechange;
xmlhttp.onreadystatechange = function()
{
oldReadyStateChange.apply(xmlhttp, arguments);
if ( this.readyState === 4 ) // completed
{
hook.requestCompleted(xmlhttp,
xmlhttp.hook_url, xmlhttp.hook_method);
}
};
oldSend.apply(this, arguments);
};
return hook;
})();
With this bit of code loaded in your userscript, you can then implement your logic as follows:
var k;
function doit()
{
k = document.getElementsByClassName("_698");
var g= Array.prototype.slice.call(k);
confirm(g.length);
}
window.addEventListener("load", function()
{
ajaxHook.requestCompleted = function(xmlhttp, url, method)
{
// is this the request we're interested in?
// (Facebook appears to load friends from a URL that contains this string)
if ( /AllFriendsAppCollectionPagelet/.test(url) )
{
// Facebook defers rendering the results here,
// so we just queue up scraping them until afterwards
setTimeout(doit, 0);
}
};
// trigger loading of more friends by scrolling the bottom into view
document.getElementById( "pageFooter" )
.scrollIntoView();
});

How do I use a custom function in background.js for a Chrome Extension?

I'm writing a Chrome extension and I want to call a function in my background.js.
This is the function:
function getUrlVars(url) {
var vars = {};
var parts = url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {
if(vars[key]){
if(vars[key] instanceof Array)
vars[key].push(value);
else
vars[key] = [vars[key], value];
}else
vars[key] = value;
});
return vars;
}
It returns a parameter of the url.
I put this function in background.js, but when I call it, it doesn't work.
I call the function here:
chrome.webRequest.onBeforeRequest.addListener(function(details){
chrome.tabs.get(details.tabId, function (tab) {
source=getUrlVars(details.url)[iapisource];
id=getUrlVars(details.url)[iapiid];
redirectUrl=tab.url+"?iapisource="+source+"&iapiid="+id;
});
return {cancel : true , redirectUrl : redirectUrl};
},
// block requests matching this url
{urls: ["*://*/*iapisource*iapiid*"]},["blocking"]);
Here I take the URL before the request and append to it the parameters of the new URL.
Where do I have to put the function and how can I call it?
I could give you a fish but I'll teach you how to fish instead.
1) You should debug your code if it doesn't work. StackOverflow is not an online debugger.
Go to chrome://extensions/, find your extensions, click on your background page, go to the Console tab and investigate the errors you see there.
2) For one thing instead of this:
source=getUrlVars(details.url)[iapisource];
I think you what you wanted is this:
source=getUrlVars(details.url)['iapisource'];
or better yet:
var params = getUrlVars(details.url);
var redirectUrl = tab.url+"?iapisource=" + params.iapisource + "&iapiid=" + params.iapiid;
3) The tabs.get callback function will only run after you've already returned from the onBeforeRequest
// 1.
chrome.webRequest.onBeforeRequest.addListener(function(details){
// 2.
chrome.tabs.get(details.tabId, function (tab) {
// 4.
source=getUrlVars(details.url)[iapisource];
id=getUrlVars(details.url)[iapiid];
redirectUrl=tab.url+"?iapisource="+source+"&iapiid="+id;
});
// 3.
return {cancel : true , redirectUrl : redirectUrl}; // redirectUrl is undefined!
},
{urls: ["*://*/*iapisource*iapiid*"]},
["blocking"]
);
You can use extension.getBackgroundPage to gain access to the window object of your background page which would normally contain your global functions in the background page.
This issue is further discussed here.
If you can't do that, you should look into message passing.

How to make a cache/memorizer in BackboneModel

Let's suppose I have a View which can make model.fetch() and then a request to the server.
I would like to implement:
1) A checker able to memorise the result
2) refresh the result (making the request to the server) only if the last request to the server is older than ten minutes.
What should I do?
Is there already a piece of code to make that?
define([], function() {
var MyModel = Backbone.Model.extend({
url: function () {
return "http://localhost/restapi/model/";
},
fetch () {
if(diffTime > 10minutes) {
// make request to the server
}
else {
// return memo
}
}
});
});
You need to override the Backbone.sync method http://documentcloud.github.com/backbone/#Sync.
This code does the saving to local storage to implement a cache http://documentcloud.github.com/backbone/docs/backbone-localstorage.html.
It is fairly simple to add some logic in the "read" case to fetch from the server if the data is older than 10 minutes.
As codemonkey said, localstorage would be a good option. But if you don't want to use a library for that, you can use this class to extend those models who require the cache functionality.
var CachedModel = Backbone.Model.extend({
lastFetch: null, // millisec.
cache: { }
fetch: function () {
if(!this.lastFetch || (lastFetch - Date.now() > 10*60*1000) {
// make request to the server
}
else {
// return this.cache
}
}
});
I have found https://github.com/Ask11/backbone.offline to work really well for me.
The only vice is that it uses localStorage, you could also opt for more support by going with rewriting bits and pieces for use with amplify.store http://amplifyjs.com/api/store/.

JS: Using variable names to call function

Im working with jQuery. I have an app that makes ajax requests to server that responds with JSON.
in some cases the response from the server will indicate the name of a JS function to be called
{"responseType":"callback", "callback":"STUFF.TestCallback","callbackData":"this is in the callback"}
If the responseType is "callback" as above the JSON is passed to a function to handle this response type. (the var "response" contains the JSON above)
STUFF.callback = function(response){
if(typeof response.callback =='function'){
console.log("All Good")
response.callback(response);
}else{
console.log("Hmm... Cant find function",response.callback );
}
}
STUFF.TestCallBack = function(data){
alert("it worked");
}
But when I do this I get the error "response.callback is not a function".
Any comments on why this is not working and how to do this properly would be greatly appreciated.
A String is a String, not a Function.
response.callback() doesn't work because it is the same as "STUFF.TestCallback"() not STUFF.TestCallback()
You probably want to have the data structured something more like "callback": "TestCallback" then then do:
STUFF[response.callback](response);
Here you use the String to access a property of STUFF. (foo.bar and foo['bar'] being equivalent.)
You could transform that "namespace.func" into a call like this:
STUFF.callback = function(response) {
var fn = response.callback.split("."), func = window;
while(func && fn.length) { func = func[fn.shift()]; }
if(typeof func == 'function') {
console.log("All Good")
func(response);
} else{
console.log("Hmm... Cant find function", response.callback);
}
}
What this does it grab the function by getting window["STUFF"] then window["STUFF"]["TestCallback"] as it loops, checking if each level is defined as it goes to prevent error. Note that this works for any level function as well, for example "STUFF.One.Two.func" will work as well.
You propably could do
var myfunction = eval(response.callback);
myfunction(response);
although using eval() is considered as a bad style by some javascript developers.

XMLHTTP.open Not working in secong Call in IE6 and Running Properly in firefox

HI i have flowing code that runs as expected in Firefox and in IE6 it runs proper at first time and when the second call made it makes problem and returns old value
function loadCartItems()
{
var xmlhttp23;
if(xmlhttp23!= 'undefined')
{
xmlhttp23=getXmlHttpObject();
}
xmlhttp23.onreadystatechange=function()
{
if(xmlhttp23.readyState==4)
{
alert(xmlhttp23.responseText);
}
}
xmlhttp23.open("GET","../printerink/ItemsInCart.aspx",true);
xmlhttp23.send(null);
xmlhttp23=null;
}
function getXmlHttpObject()
{
var request = null;
/* Does this browser support the XMLHttpRequest object? */
if (window.XMLHttpRequest) {
if (typeof XMLHttpRequest != 'undefined')
/* Try to create a new XMLHttpRequest object */
try {
request = new XMLHttpRequest( );
} catch (e) {
request = null;
}
/* Does this browser support ActiveX objects? */
} else if (window.ActiveXObject) {
/* Try to create a new ActiveX XMLHTTP object */
try {
request = new ActiveXObject('Msxml2.XMLHTTP');
} catch(e) {
try {
request = new ActiveXObject('Microsoft.XMLHTTP');
} catch (e) {
request = null;
}
}
}
return request;
}
here i am going to alert the result i have checked at every place by using alert every thing is working proper but i just noticed one problem that is as i am using aspx page to return result in this aspx page i set the break points for debug and found that when first time page get loads these break points fire and if the page loading 2nd time from same IE6 window they are not fired and one thing more that is XMLHTTP all things are working like xmlhttp.readyState is 4 and xmlhttp.status is 200 and just only the xmlhttp.open seems that it is not executing
plz help
Frankly I am confused by your code. I think some clarification is needed before it is possible to help. First off, what is the intention of this:
function loadCartItems() {
var xmlhttp23;
if(xmlhttp23!= 'undefined') {
xmlhttp23=getXmlHttpObject();
}
...
xmlhttp23=null;
}
I mean, by definition, the local variable xmlhttp23 will always be undefined whenever you enter loadCartItems(). Then you test xmlhttp23!= 'undefined' but this doesn't really make sense: xmlhttp23 will never be equal to the string literal 'undefined'.
I don't understand the last line xmlhttp23=null either: is it your intention to explicitly clean up the XMLHttpRequest object? It seems to me this isn't really necessary, because the local variable xmlhttp23 will be out of scope anyway after the loadCartItems() function finishes.
Looking at the initialization code for xmlhttp23, it almost looks like you intended to create a XMLHttpRequest just once, and want to reuse that. If that is the case, I think your code should be:
var xmlhttp23;
function loadCartItems() {
if(!xmlhttp23) {
xmlhttp23 = getXmlHttpObject();
}
xmlhttp23.open("GET","../printerink/ItemsInCart.aspx",true);
xmlhttp23.onreadystatechange = function() {
if (xmlhttp23.readyState==4) {
if (xmlhttp23.status==200) { //success
alert(xmlhttp23.responseText);
}
else { //error
alert("Whoops: " + xmlhttp23.statusText);
}
}
}
xmlhttp23.send(null);
}
Note that the onreadystatechange handler must be assigned after calling the open() method. If you don't, you can't reuse the Xhr object in IE6.
Read more about why that is here:
http://keelypavan.blogspot.com/2006/03/reusing-xmlhttprequest-object-in-ie.html

Categories

Resources