Chrome - annoying window.onpopstate loading cursor - google-chrome

I'm running a single-page application which loads all <a> links via AJAX. This is a minor visual annoyance, but ever since I switched from #hash tag navigation to proper history via window.onpopstate and window.history.pushState(), many clicks in the system will cause the cursor to "load" as if it were a standard non-AJAX page. On Windows, I get the pointer with the loading symbol. This is in contrast to before where it would simply load the AJAX into the container and never change the cursor. I feel this "breaks the immersion" of being a single-page application.
My code is simple:
$('body').on("click", 'a', function() {
var url = $(this).attr('href');
$.ajax({type:'GET', url: url, success: function(data) {
window.history.pushState({}, url, url);
$('#content').html(data);
}});
return false;
}
});
window.onpopstate = function(event) {
run_url();
};
This works as intended in Firefox. If I remove window.onpopstate completely, it no longer does this, but I cannot find an alternative to onpopstate.

Related

How to avoid Internet Explorer pausing while Ajax GET completes

I have a situation where I'm trying to load JSON data into a popup using AngularJS and Bootstrap. It loads fine in Chrome, Edge, and other browsers I've tested: the popup appears, then there is a spinner that shows until the content loads. But in Internet Explorer, when I click to load the popup, it appears that all scripts on the page pause until all of the data is received. For several seconds, nothing appears to be happening; then, the popup shows up with the requested content. We are using classic ASP to serve the data.
I have tried setting a timeout and now the popup will show up, but once the call starts, the spinner freezes until the data shows up.
I was wondering if anyone else has encountered this and knows of a workaround? This is the simplified version of what I have so far:
$scope.loadData = function() {
if (!isLoaded) {
$scope.loading = true;
$timeout(function(){
$http({
method: 'GET',
url: '/get_data.asp'
}).then(function(res) {
$scope.data = res.data.data;
$scope.loading = false;
});
}, 500);
}
}

URL bar not updated in Chrome after Backbone routing

I have a Backbone application which, at one point, opens a new tab in the browser. After the execution in the new tab is complete a javascript will be triggered (in that new tab) to trigger routing in the opener window. Javascript code looks like this:
window.onunload = window.onbeforeunload = function(e){
opener.router.navigate("start",{trigger: true});
};
window.close();
This works great, the 'start' route is executed and the correct result is shown in all browsers (including Chrome). But in Chrome, the url bar is not updated with the new url (eg. ../something#start), instead the original url for the opening window remains in the address bar.
In IE and Firefox the url bar shows the correct url. Is there some way to achieve this behaviour in Chrome also?
Any input appreciated!
Instead of trying to make a call to the router directly from the tab that's about to be closed, have you tried triggering a Backbone event (which the "opener window" would be listening to) instead?
So change:
window.onunload = window.onbeforeunload = function(e){
opener.router.navigate("start",{trigger: true});
};
window.close();
to:
window.onunload = window.onbeforeunload = function(e){
Backbone.trigger('routeChange');
};
window.close();
And include a listener to the routeChange event in the "opener window's" view with a callback function which executes router.navigate():
opener.listenTo(Backbone, 'routeChange', function(e) {
opener.router.navigate('start',{trigger: true});
});
Let me know if that helps.

Chrome Extension: Insert a clickable image using a content script

I know hat it is possible, but I am not quite sure how to do it the 'right' way, as to ensure there are no conflicts.
I came across this question: Cannot call functions to content scripts by clicking on image . But it is so convoluted with random comments that it's hard to understand what the corrected way was.
Use case:
Html pages have a div on the page where they expect anyone using the Chrome extension to inject a picture. When users click on he picture, I want to somehow notify an event script. So I know I need to register a listener so the code inserted messages the event script.
Can I get some indication on what code to inject through the content script? I saw that sometimes injecting jquery directly is advised.
I am trying to avoid having the html page to post a message to itself so it can be intercepted. Thanks
With the help of Jquery something like this would capture the image onclick event and allow you to pass a message to a background page in the Chrome Extension:
$("img").click(function(){
var imageSrc = $(this).attr("src");
//Post to a background page in the Chrome Extension
chrome.extension.sendMessage({ cmd: "postImage", data: { imgSrc: imageSrc } }, function (response) {
return response;
});
});
Then in your background.js create a listener for the message:
chrome.extension.onMessage.addListener(
function (request, sender, sendResponse) {
if (request.cmd == "postImage") {
var imageSrc = request.data.imgSrc;
}
});

How to show html files with modal.open using jquery?

Currently i use a fine working code for opening a modal with Jquery :
$(document).ready(function(){
$("span.ico-detail").click(function(){
modal.open({content: "View detail of " + $(this).parent().parent().attr("id")});
e.preventDefault();
});
});
And now the problem is : How can I use modal.open to open a HTML file named "view.html", which contaning the string of "View detail of "?
What should I change the content : "xxx" with, so I can open the HTML file (view.html) and join it with other text ?
Thanks before.
If the view.html is stored on a server and its content is static, then you can choose to preload the content of the file using ajax.
$(function () {
window.myAppNs = {
viewContent: null;
};
$.ajax({
url: 'view.html',
dataType: 'html',
type: 'GET'
}).done(function (resp) {
myAppNs.viewContent = resp;
});
$("span.ico-detail").click(function(){
modal.open({content: myAppNs.viewContent + $(this).parent().parent().attr("id")});
e.preventDefault();
});
});
I am creating a global variable myAppNs. This will hold all app related variables. The idea is not pollute the global namespace with unnecessary variables. There are better and safer ways to create a namespace. If that interests you, you can google for the same.
The ajax call preloads the content of the view.html and stores it in myAppNs.viewContent. The click handler reads that content from the variable.
There is a slight chance that the user can click the element before the ajax response is returned. If that's an issue, you can always move the namespace creation and ajax call out of document.ready and place it in the head section, immediately after referencing jquery. That ought to give the browser enough time to fetch the content before the dom is ready, but there is still that small possibility that the response might be delayed. If you need to ensure the user can click only if the data has been fetched, then bind the click handler inside the done callback of the ajax call.

Using jQuery.getJSON in Chrome Extension

I need to do a cross-domain request in a chrome extension. I know I can it via message passing but I'd rather stick to just jQuery idioms (so my javascript can also work as a <script src="">).
I do the normal:
$.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?", function(data) {
console.log(data);
});
but in the error console I see:
Uncaught ReferenceError: jsonp1271044791817 is not defined
Is jQuery not inserting the callback function correctly into the document? What can I do to make this work?
(If I paste the code into a chrome console, it works fine, but if I put it as the page.js in an extension is when the problem appears.)
Alas, none of these worked, so I ended up doing the communication via the background.html.
background.html
<script src="http://code.jquery.com/jquery-1.4.2.js"></script>
<script>
function onRequest(request, sender, callback) {
if (request.action == 'getJSON') {
$.getJSON(request.url, callback);
}
}
chrome.extension.onRequest.addListener(onRequest);
</script>
javascripts/page.js
chrome_getJSON = function(url, callback) {
console.log("sending RPC");
chrome.extension.sendRequest({action:'getJSON',url:url}, callback);
}
$(function(){
// use chrome_getJSON instead of $.getJSON
});
If you specify "api.flickr.com" in your manifest.json file you will not need to use the JSONP callback, script injection style of cross domain request.
For example:
"permissions": ["http://api.flickr.com"],
This should work beautifully in you code. I would remove the querystring parameter "&jsoncallback" as there is no JSONP work needed.
The reason why your current code is not working is your code is injecting into pages DOM, content scripts have access to the DOM but no access to javascript context, so there is no method to call on callback.
My impressions it that this fails because the jQuery callback function is being created within the 'isolated world' of the Chrome extension and is inaccessible when the response comes back:
http://code.google.com/chrome/extensions/content_scripts.html#execution-environment
I'm using Prototype and jQuery for various reasons, but my quick fix should be easy to parse:
// Add the callback function to the page
s = new Element('script').update("function boom(e){console.log(e);}");
$$('body')[0].insert(s);
// Tell jQuery which method to call in the response
function shrink_link(oldLink, callback){
jQuery.ajax({
type: "POST",
url: "http://api.awe.sm/url.json",
data: {
v: 3,
url: oldLink,
key: "5c8b1a212434c2153c2f2c2f2c765a36140add243bf6eae876345f8fd11045d9",
tool: "mKU7uN",
channel: "twitter"
},
dataType: "jsonp",
jsonpCallback: callback
});
}
// And make it so.
shrink_link('http://www.google.com', "boom");
Alternatively you can try using the extension XHR capability:
http://code.google.com/chrome/extensions/xhr.html
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// JSON.parse does not evaluate the attacker's scripts.
var resp = JSON.parse(xhr.responseText);
}
}
xhr.send();
The syntax is a little off. There's no need for the callback( bit. This works flawlessly. Tested in the javascript console of Chrome on this StackOverflow page (which includes jQuery):
$.getJSON("http://api.flickr.com/services/feeds/photos_public.gne?tags=cat&tagmode=any&format=json&jsoncallback=?", function(data) {
console.log(data);
});
As many of you will know, Google Chrome doesn't support any of the handy GM_ functions at the moment.
As such, it is impossible to do cross site AJAX requests due to various sandbox restrictions (even using great tools like James Padolsey's Cross Domain Request Script)
I needed a way for users to know when my Greasemonkey script had been updated in Chrome (since Chrome doesn't do that either...). I came up with a solution which is documented here (and in use in my Lighthouse++ script) and worth a read for those of you wanting to version check your scripts:
http://blog.bandit.co.nz/post/1048347342/version-check-chrome-greasemonkey-script