I have a javascript file from a wordpress plugin that I'm trying to modify. It seems to be getting away with cross-domain requests with some sort of loophole. It's this function:
function urlCallback(url){
var req = url;
var head = document.getElementsByTagName("head").item(0);
var script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.setAttribute("src", req);
head.appendChild(script);
}
The url passed is something like 'http://url.com/page?callback=namespace.myFunction' where myFunction is a function defined elsewhere in the script.
From what I understand, this is inserting a source url at the top of my page, causing the browser to load that page. The callback attached to that url is then called, passing the result as a single parameter to the example function, myFunction.
My problem is that I need to call myFunction for a few different unique urls, but the results are tied to the calling url. myFunction seems to be called whenever the page finishes loading, so I can't simply keep count of which data set it's operating on. I need some way for myFunction to either be passed an additional variable encoded in the callback url, or for myFunction to know the url it was attached to.
Can anyone help me?
EDIT:
To elaborate further, here is a simplified version of the code I have:
var parseUrls = (function(){
function urlCallback(url){
var req = url;
// Here is where I need to save the url
var head = document.getElementsByTagName("head").item(0);
var script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.setAttribute("src", req);
head.appendChild(script);
}
return {
options: {},
parseNextUrl: function(result) {
if (!result || !result.data) { return; }
var data = result.data;
// Here is where I need the url
},
parseUrl: function(result) {
if (!result || !result.data) { return; }
var data = result.data;
for (var i = 0; i < data.length; i++) {
urlCallback( data[i].url + "/new_url/page?callback=parseUrls.parseNextUrl" );
}
},
showResult: function(options){
urlCallback( "http://start.url.com/page?callback=parseUrls.parseUrl" );
this.options = options;
}
};
})();
Just to be clear, parseNextUrl is called whenever the source request is finished. Which means all the urlCallback calls have already finished by then.
Here's the updated code based on the newly provided code.
var parseUrls = (function(){
function urlCallback(url){
// Create request
var head = document.getElementsByTagName("head").item(0);
var script = document.createElement("script");
script.setAttribute("type", "text/javascript");
script.setAttribute("src", req);
head.appendChild(script);
}
return {
parsers: [], //response handler array
options: {},
parseUrl: function(result) {
//parseUrls.parseUrl.url = request URL
if (!result || !result.data) { return; }
var data = result.data;
// Create requests
for (var i = 0; i < data.length; i++) {
// Create new response handler
var parseNextUrl = function(result) {
// parseNextUrl.url = request URL
if (!result || !result.data) { return; }
var data = result.data;
// Check the URL
console.log('Result URL = ' + parseNextUrl.url);
};
// Make callback names and URLs for each handler
var cbName = "parseUrls.parsers[" + this.parsers.length + "]";
var req = data[i].url + "/new_url/page?callback=" + encodeURI(cbName);
// Save the URL in the handler
parseNextUrl.url = req;
// Put handler into storage.
// Note: Don't delete/insert any of parsers array element
// until no more new requests and all responses are received.
this.parsers.push(parseNextUrl);
urlCallback(req);
}
},
showResult: function(options){
this.parseUrl.url = "http://start.url.com/page?callback=parseUrls.parseUrl";
urlCallback(this.parseUrl.url);
this.options = options;
}
};
})();
Related
Trying to capture response of a async request in dojo/aspect before() event before handing it off to the original method as below:
aspect.before(ecm.model.SearchTemplate.prototype, "_searchCompleted", function(response, callback, teamspace){
var args = [];
if(response.num_results==0 && isValidQuery){
var args = [];
var requestParams = {};
requestParams.repositoryId = this.repository.id;
requestParams.query = query;
Request.invokePluginService("samplePlugin", "sampleService",
{
requestParams: requestParams,
requestCompleteCallback: lang.hitch(this, function(resp) { // success
//call stack doesnt enter this code block before returning params to the original
//function
resp.repository = this.repository;
args.push(resp);
args.push(callback);
args.push(teamspace);
})
}
);
return args; //args is empty as the response is not captured here yet.
}
});
aspect.around is what you're looking for. It will give you a handle to the original function you can call at will (thus, async at any time you're ready - or never at all).
aspect.around(ecm.model.SearchTemplate.prototype, "_searchCompleted", function advisingFunction(original_searchCompleted){
return function(response, callback, teamspace){
var args = [];
if(response.num_results==0 && isValidQuery){
var args = [];
var requestParams = {};
requestParams.repositoryId = this.repository.id;
requestParams.query = query;
Request.invokePluginService("samplePlugin", "sampleService",
{
requestParams: requestParams,
requestCompleteCallback: lang.hitch(this, function(resp) { // success
//call stack doesnt enter this code block before returning params to the original
//function
resp.repository = this.repository;
args.push(resp);
args.push(callback);
args.push(teamspace);
original_searchCompleted.apply(this,args);
})
}
);
}
}
});
I have been having some issues with opening multiple webpages in phantomjs, I am first opening a website which contains a few links, which I want to open as well, and save a piece of text from each URL to my jobs_list which has many objects inside of it. And after all the URL's have been run, I want to exit phantomjs. But as it is right now it never exits, and I have trouble recieving data from second function.
var webPage = require('webpage');
var page = webPage.create();
var jobs_list = [];
page.open('url', function (status) {
page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function() {
page.onConsoleMessage = function(msg) {
console.log(msg);
};
var list = page.evaluate(function() {
var jobs = [];
var job;
$('.test').each(function(){
$(this).find('span').each(function(){
var job_link = $(this).find('a');
var url = job_link.attr("href");
job = {title : job_link.text(), url : url, location : ""};
jobs.push(job);
})
});
return jobs;
});
var i = 0;
jobs_list = list;
next_page(i);
});
});
function next_page(i){
if (i <= (jobs_list.length-1)) {
var current_job = jobs_list[i];
var url = current_job.url;
page.open(url, function (status) {
page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function () {
var location = page.evaluate(function() {
var job_location;
$('.job-location').each(function(){
$(this).find('li').each(function(){
job_location = $(this).text();
})
})
console.log(job_location);
return job_location;
});
jobs_list[i].location = location;
if(i == (jobs_list.length-1)) {
phantom.exit(0);
}
});
});
console.log(i, current_job.title);
next_page(++i);
}
}
The problem is that the page.open call is asynchronous. If you look closely to your next_page function it can be shortened to this:
function next_page(i){
if (i <= (jobs_list.length-1)) {
var current_job = jobs_list[i];
var url = current_job.url;
page.open(url, function (status) {
...
});
console.log(i, current_job.title);
next_page(++i);
}
}
It means that next_page(++i); is executed before page.open(url, ...) even managed to load the first HTML content. This call leads to the next page.open(url, ...) being executed immediately, thus overwriting the previous request. And you're never going to get any data this way.
You have to do two things:
move the next_page(++i); call where the execution of one page is finished
reduce the number of condition checking
I propose:
function next_page(i){
if (i <= (jobs_list.length-1)) {
var current_job = jobs_list[i];
var url = current_job.url;
page.open(url, function (status) {
page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function () {
var location = page.evaluate(function() {
var job_location;
$('.job-location').each(function(){
$(this).find('li').each(function(){
job_location = $(this).text();
})
})
console.log(job_location);
return job_location;
});
jobs_list[i].location = location;
console.log(i, current_job.title);
next_page(++i);
});
});
} else {
phantom.exit(0);
}
}
That's quite an old version of jQuery. Perhaps you want to load a newer version. If the page already has jQuery included, you will likely break the page by loading another jQuery into it. Don't load an additional jQuery version at all in this case.
Inside a web worker, I have an html string like:
"<div id='foo'> <img src='bar'></img> <ul id='baz'></ul> </div>"
Is there any library I can import to easily access id and src attributes of the different tags ? Is regex the only way inside a worker ?
There are two ways to solve this problem efficiently:
Regex
With the risk of getting false positives, you can use something like:
var pattern = /<img [^>]*?src=(["'])((?:[^"']+|(?!\1)["'])*)(\1)/i;
var match = string.match(pattern);
var src = match ? match[2] : '';
Built-in parser & messaging
If getting the HTML right is a critical requirement, just let the browser parse the HTML, by passing the string to the caller. Here's a full example:
Caller:
var worker = new Worker('worker.js');
worker.addEventListener('message', function(e) {
if (!e.data) return;
if (e.data.method === 'getsrc') {
// Unlike document.createElement, etc, the following method does not
// load the image when the HTML is parsed
var doc = document.implementation.createHTMLDocument('');
doc.body.innerHTML = e.data.data;
var images = doc.getElementsByTagName('img');
var result = [];
for (var i=0; i<images.length; i++) {
result.push(images[i].getAttribute('src'));
}
worker.postMessage({
messageID: e.data.messageID,
result: result
});
} else if (e.data.method === 'debug') {
console.log(e.data.data);
}
});
worker.js
// A simple generic messaging API
var callbacks = {};
var lastMessageID = 0;
addEventListener('message', function(e) {
if (callbacks[e.data.messageID]) {
callbacks[e.data.messageID](e.data.result);
}
});
function sendRequest(method, data, callback) {
var messageID = ++lastMessageID;
if (callback) callbacks[messageID] = callback;
postMessage({
method: method,
data: data,
messageID: messageID
});
}
// Example:
sendRequest('getsrc',
'<img src="foo.png">' +
"<img src='bar.png'>" +
'<textarea><img src="should.not.be.visible"></textarea>',
function(result) {
sendRequest('debug', 'Received: ' + result.join(', '));
}
);
is their any way to monitor all the request made on the page triggered by script, click or anything else, it should not dependant on any script block or code just monitor what ever the request made, using jquery and javascript
example:
// monitor all the request made on the page.
monitor{
success:function(){
}
error:function(){
}
}
You can not track all the requests made on the webpage. However, you can track the requests that were made using jQuery by replacing $.ajax bay a wrapper.
Sample replacement plugin:
(function($, undefined) {
// a private variable which will store the current active monitors
var monitors = [];
// a public API to add a monitor.
$.monitorAjax = function(monitor) {
monitors.push(monitor);
};
// here starts the implementation.
// a function to wrap a callback (error or success) to make monitors functions called.
var wrapCallback = function(name, settings) {
return function() {
for(var i = 0; i < monitors.length; i++) {
var monitor = monitors[i];
if(monitor[name] != null) monitor[name].apply(this, arguments);
}
if(settings[name] != null) settings[name].apply(this, arguments);
};
};
// replace $.ajax by a wraped version which will replace success and error callbacks by wrappers.
// note that you may also track calls and their settings if you want.
var unwrappedAjax = $.ajax;
$.ajax = function(url, settings) {
if(settings == null) settings = {};
var wrappedSuccess = wrapCallback("success", settings);
var wrappedError = wrapCallback("error", settings);
var wrappedSettings = $.extend({}, settings, {success: wrappedSuccess, error: wrappedError});
return unwrappedAjax(url, wrappedSettings);
};
})(jQuery);
In jQuery maybe this:
var original_jquery_ajax=$.ajax;
$.ajax=function(){
var a_fn, a_url;
var cb=function(data, status, settings){
a_fn(data, status, settings);
console.log(a_url, data); // <-- here
}
for(var i=0; i<arguments.length; i++)
if(arguments[i] instanceof Object){
if(arguments[i].success){
a_fn=arguments[i].success; arguments[i].success=cb;
}
if(arguments[i].url) a_url=arguments[i].url;
}
if(typeof(arguments[0])=="string") a_url=argements[0];
var aj=original_jquery_ajax.apply(null,arguments);
var done_original=aj.done;
aj.done=function(cb_fn){
a_fn=cb_fn;
done_original(cb);
return aj;
};
return aj;
};
Now, when use $.ajax(url), you have in console the url and the returned data
Thanks for reading this.
I imagine this is really a javascript question, and my title probably does not get at the heart of what I am trying to do, but I want to store the result of my ajax request in a global variable. This would allow me to test the var before making the ajax call...and avoid repeated ajax calls for the same data. I need to be able to pass the variable name from the click event down through the populateSelect function into the ajaxCall function.
It seems like I could pass a function as a parameter, but I have not been able to make that work.
I like to include working examples in my questions, but in this case the latency in the call to the server is part of the problem.
Thanks
$('#getSelectOptions').bind("click", function() {
populateSelect(this);
});
function populateSelect(whatWasClicked) {
var thisSelect = $(whatWasClicked).parents("div").find("select") ;
var before = function() { $(loading).show() ; } ;
var complete = function() { $(loading).hide() ; } ;
var data = {'_service' : 'myService', '_program' : 'myProgram' } ;
var error = function(){alert("Error"); } ;
var success = function(request) { $(thisSelect).html(request) ; };
var waitTime = 20000 ;
ajaxCall(thisSelect, waitTime, before, complete, data, success, error ) ;
}
function ajaxCall(elementToPopulate, waitTime, whatToDoBeforeAjaxSend,
whatToDoAfterAjaxSend, dataToSendToTheServer,
whatToDoAfterSuccess, whatToDoAfterError) {
$.ajax({
type: "post",
url: "http://myURL/cgi-bin/broker",
dataType: "text",
data: dataToSendToTheServer,
timeout: waitTime,
beforeSend: whatToDoBeforeAjaxSend,
error: whatToDoAfterError(request),
success: whatToDoAfterSuccess(request)
});
}
EDIT Further education in how to write a good question... I should have mentioned that I call populateSelect to populate multiple selects..so I need way to reference the results for each select
jQuery has a $.data method which you can use to store/retrieve items related to any element on the page.
//e.g. create some object
var inst = {};
inst.name = 'My Name'
var target = $('#textbox1');
//save the data
$.data(target, 'PROP_NAME', inst);
//retrieve the instance
var inst = $.data(target, 'PROP_NAME');
It looks like in the example you gave, you only have one type of AJAX request, POSTed to the same URL with the same data every time. If that's the case, you should just need something like :
var brokerResponse = null; // <-- Global variable
function populateSelect(whatWasClicked) {
var thisSelect = $(whatWasClicked).parents("div").find("select") ;
if (!brokerResponse) { // <-- Does an old response exist? If not, get one...
var before = function() { $(loading).show() ; } ;
var complete = function() { $(loading).hide() ; } ;
var data = {'_service' : 'myService', '_program' : 'myProgram' } ;
var error = function(){alert("Error"); } ;
var success = function(request) { // <-- Store the response before use
brokerResponse = request;
$(thisSelect).html(brokerResponse);
};
var waitTime = 20000 ;
ajaxCall(thisSelect, waitTime, before, complete, data, success, error ) ;
}
else { // <-- If it already existed, we get here.
$(thisSelect).html(brokerResponse); // <-- Use the old response
}
}
If you have multiple possible items for whatWasClicked which each need a different AJAX response cached, then you need to have some string with which to identify whatWasClicked, and use that to store multiple values in your global variable. For example, if you have a unique id on whatWasClicked, this would work:
var brokerResponse = {}; // Global variable is a simple object
function populateSelect(whatWasClicked) {
var whatWasClickedId = $(whatWasClicked).attr('id'); // Get the unique ID
var thisSelect = $(whatWasClicked).parents("div").find("select") ;
if (!brokerResponse[whatWasClickedId]) { // Check that ID for a response
var before = function() { $(loading).show() ; } ;
var complete = function() { $(loading).hide() ; } ;
var data = {'_service' : 'myService', '_program' : 'myProgram' } ;
var error = function(){alert("Error"); } ;
var success = function(request) {
brokerResponse[whatWasClickedId] = request; // Using ID
$(thisSelect).html(brokerResponse);
};
var waitTime = 20000 ;
ajaxCall(thisSelect, waitTime, before, complete, data, success, error ) ;
}
else {
$(thisSelect).html(brokerResponse[whatWasClickedId]); // Etc...
}
}
JavaScript's scoping is such that if you just declared a global variable, you should be able to access it from within the ajax success function and the click function as well.
var _global_holder = null;
$('#getSelectOptions').bind("click", function() {
if(_global_holder==null) { whatever }
populateSelect(this);
});
function populateSelect(whatWasClicked) {
if(_global_holder !== null) {
whatever
} else { whatever else }
ajaxCall(thisSelect, waitTime, before, complete, data, success, error ) ;
}
function ajaxCall(elementToPopulate, waitTime, whatToDoBeforeAjaxSend,
whatToDoAfterAjaxSend, dataToSendToTheServer,
whatToDoAfterSuccess, whatToDoAfterError) {
...
}