I'm writing my first simple Chrome extension which should organize downloads into subfolders based on the title of the tab they're downloaded from. I'm planning on extending this in future, but for the time being I can't even get this simple functionality to work. The problem seems to be that the 'filename suggest' function cannot be called from within the asynchronous tab query. Code below is the full contents of the background JavaScript file:
chrome.downloads.onDeterminingFilename.addListener(function (item, __suggest) {
//Find active tab
chrome.tabs.query({ active: true }, function (tabs) {
var activeTab = tabs[0];
//Generate filepath
var filepath = activeTab.title + "/" + item.filename;
//TODO: Sanitize filepath.
//Suggest filename for this download.
__suggest({ filename: filepath });
})
});
The error logged by the console is:
suggestCallback may not be called more than once. (extensions::downloads:42)
I've checked that the suggest function (in my JS) is definitely only called once. Any ideas how I can fix/work around this?
As the documentation says:
If the listener calls suggest asynchronously, then it must return true.
So you need to add return true after your call to chrome.tabs.query.
Related
For my Google Chrome extension, I need to grab the current URL. I know that, in regular JavaScript, I can just use this to get the link and host:
var hostname = window.location.hostname;
var url = window.location.href;
But how can I do that within an extension? I actually got it to work by using this as an example, but it seems a bit overkill for what I want to do. Is there a better way to get the current URL, maybe without injecting a script into the page?
You can use chrome.tabs.query() to get the current URL into a variable inside your extension code:
chrome.tabs.query({'active': true, 'lastFocusedWindow': true}, function(tabs) {
var url = tabs[0].url;
});
Remember: the part of your code that'll use the url variable should be inside the (anonymous) callback function above, not elsewhere. Otherwise, you'll get an undefined value, as Chrome extension code is executed asynchronously.
I'm trying to somewhat replicate what I saw in this question, particularly in this answer, but not quite the same.
My intent is, if the zip has no files (it can happen because the folder could be empty) I want to return an alert just so the user is warned that is not possible to obtain the file at the time.
But I'm missing on the redirection point, I don't want the alert to redirect the user to a blank page refering the Action, I want it to stay in the page, also due to some filters.
Is this possible? I couldn't find anything that would stop the redirection from happening.
Here is my the Action Controller code:
public ActionResult DownloadZip(List<int> things)
{
// Create zip with files
if (!zip.Any())
{
return Content(#"<script language='javascript' type='text/javascript'>
alert('Message');
</script>
");
}
// Return zip
}
Here is the call from the view:
$("#btnExportToZip").on("click", function (e) {
var grid = $("#gridThings").data("kendoGrid");
var items = grid.dataSource.data();
var lstIds = [];
$.each(items, function (index, elem) {
if (elem.Checked) {
lstIds.push(elem.Id);
}
});
if (lstIds.length > 0) {
var params = lstIds.join("&listAmostras=")
var url = '/Search/DownloadZip?listAmostras=' + params;
window.location.href = url;
}
});
If you do a redirect as you're doing here, it's too late to take it back once you've determined the zip file is empty. Your best bet here is probably to do an AJAX file download. Bear in mind, though, that this will require that the browser supports the HTML5 File API, so IE 9 and under are out.
$.ajax({
url: url,
async: false,
xhrFields: {
responseType: 'blob'
},
success: function (data) {
var a = document.createElement('a');
var url = window.URL.createObjectURL(data);
a.href = url;
a.download = 'myfile.pdf';
a.click();
window.URL.revokeObjectURL(url);
}
});
Essentially what this does is request the zip file via AJAX. Once the file data has been received, an anchor link is added to the DOM (not visible) and dynamically "clicked" to approximate the behavior of user click a link to a static file. In other words, a download prompt will pop as soon as the AJAX request completes successfully. However, this code only removes the need to redirect. You still need to conditionally pop the download only if the zip file has something in. There's two ways you can accomplish that.
In the success callback of the AJAX, you would wrap the code there in a conditional that checks that data.size > 0. However, that might not actually work. I've never looked at an empty zip file, but it's entirely possible that there's file headers in the binary that would cause the blob to actually have a size greater than zero, even though it's "empty".
The better approach is to return an error response in your zip action when the zip file is empty. Off the top of my head, I'm not sure what the most appropriate error response code would be, but anything in 400-500 range will work for triggering the appropriate AJAX callback. Then, you just need to add and error handler to this AJAX. In that handler, you could then notify the user however you like that there's no download because the zip would be empty.
As per my understanding, alert is redirect the user to the blank page because in the javascript you have the line window.location.href = url; which might be redirect to the same action again which shows the alert.
So try to give the different url to the window.location.href
for ex:window.location.href = '../somecontroller/someaction';
thanks
Karthik
This is similar to: How to open a file using JavaScript?
Goal: to retrieve/open a file on an image's double click
function getFile(filename){
// setting mime this way is for example only
var mime = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
jQuery.ajax({ url : 'get_file.pl',
data : {filename:filename},
success : function(data){
var win = window.open('','title');
win.document.open(mime);
win.document.write(data);
win.document.close();
}
});
}
jQuery('#imgID').dblclick(function(){
getFile('someFile.docx');
});
I'm doing this off the top of my head, but I think the above would work for text files, but not binary. Is there a plugin that does this properly? The ideal would be to open the file in the browser (or application), rather than download, but I doubt that is a dream. If the file must be downloaded with the save/open dialog, that's fine.
Edit:
One piece of information that I forgot to mention is that I'd like this to be a POST request. This is partly why I was looking at AJAX to begin with. I've seen workarounds that have created forms/iframes to do something similar, but I was looking for a better handler of the returned info.
Seems to me there's no reason to do this via AJAX. Just open the new window to get_file.pl?filename=... and let the browser handle it. If the user has a plugin capable of handling the Content-Type sent by get_file.pl, the file will display; otherwise, it should download like any other file.
function getFile(filename) {
window.open('get_file.pl?filename=' + filename,'title');
}
jQuery('#imgID').dblclick(function() {
getFile('someFile.docx');
});
Edit: If you want to POST to your script, you can do it with some <form> hackery:
function getFile(filename) {
var win = 'w' + Math.floor(Math.random() * 10000000000000);
window.open('', win,'width=250,height=100');
var f = $('<form></form>')
.attr({target: win, method:'post', action: 'get_file.pl'})
.appendTo(document.body);
var i = $('<input>')
.attr({type:'hidden',name:'filename',value:filename})
.appendTo(f);
f[0].submit();
f.remove();
}
Of course, this is somewhat silly since it is impossible to hide your data from "prying eyes" with developer tools. If your filename really is sensitive, issue access tokens to the client, and look up the data in your sever script.
I'm using Andrew Valums' Ajax Upload plugin (GitHub link). Here is some code from it:
qq.getUniqueId = (function(){
var id = 0;
return function(){ return id++; };
})();
It's kind of a long story, but I'm in a situation where, under certain circumstances, I'd like the qq.getUniqueId function to start with an ID other than 0. It can still increment by one; it just has to start with something other than 0. What's the best way to do that?
Here are the steps to create a test environment:
Download the plugin: http://github.com/valums/file-uploader/zipball/master
Unzip it and move the "client" folder onto a web server.
Open the "demo.htm" file in a text editor, search for action: 'do-nothing.htm', and add onComplete: function(id, fileName, responseJSON) {alert(id)}, right after that.
Open the "demo.htm" file in a web browser. Be sure to access it through a web server (as opposed to just opening the local file) or else it won't work.
Upload a file. It should alert a "0" after the upload finishes. See if you can modify it so that I can pass in a different starting number.
Thanks!
Try replacing the function with one that calls the original, but adds an offset:
function offsetUniqueId(n) {
var old = qq.getUniqueId;
qq.getUniqueId = function() {
return old() + n;
}
}
See http://jsfiddle.net/alnitak/gWjqX/
From my knowledge it is not possible directly by getting tab.url (only possible in the popup.html) and doing message passing also requires that popup.html be open. Is there anyway to bypass this and get the current page url from background.html?
My best shot was with message passing, which I used this code in background.html
var bg = chrome.extension.getPopupPage();
var myURL = bg.myURL;
then in popup.html I had:
chrome.tabs.getSelected(null, function(tab) {
var myURL = tab.url;
})
Anyways the above does't work at all. Anybody know of a way to do this without having to actually open up the popup?
chrome.tabs.query is supported from background pages, of course as long as you have the tabs permission. This is the supported route as of Chrome 19.
chrome.tabs.query({
active: true,
currentWindow: true
}, function(tabs) {
var tab = tabs[0];
var url = tab.url;
});
Note that currentWindow is needed because it would otherwise return the active tab for every window. This should be guaranteed to only return one tab.
Of course, keep in mind that this is an asynchronous API – you can’t access any data it provides except from within the callback function. You can store values (such as url here) at a higher scope so another function can access it, but that will still only provide the correct result after the callback is executed.
(The below is my original answer kept for posterity – this method is no longer necessary, requires an always-running background page, and getSelected() is deprecated.)
First put this in background.html and make the myURL variable global:
var myURL = "about:blank"; // A default url just in case below code doesn't work
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { // onUpdated should fire when the selected tab is changed or a link is clicked
chrome.tabs.getSelected(null, function(tab) {
myURL = tab.url;
});
});
Then run this in popup.html when you want to get the page url:
chrome.extension.getBackgroundPage().myURL;
So if I were to make that appear inside the popup and I went to Google and clicked your page or browser action, I'll see http://google.com/webhp in the popup.
Upon seeing this post I felt that there should be a way to mark a discussion as "obsolete".
Reasons being...
This question needs to migrate to manifest v2 and...
The answers both are not working. I am using a select onchange and posting the current tab's url which is not working.
Might be these all worked in manifest v1.
My answer is ...
var myURL = "not set yet";
window.addEventListener('load', function () {
chrome.tabs.getSelected(null,function(tab){
myURL=tab.url;
});
This is a little more work but works like a charm...
I would use a content script; it's relatively simple & allows you to get any info from current page you might want. Have the background page "inject" the script into the current webpage to gather the info you need. The script then just passes it back to the background.
background.js:
// Icon is clicked and triggers content script to be injected into current webpage
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(null, { file: 'inject.js' });
});
// Listens for message back from content script and then runs
chrome.runtime.onMessage.addListener(function (request) {
var URL = request.url;
});
inject.js (content script):
// Gathers up in the information that you need from webpage
var pageInfo = {
"url": window.location.href
};
// Sends the information back to background.js
chrome.runtime.sendMessage(pageInfo);
Hope this helps someone!
chrome.tabs.getSelected(null, function(tab) {
var myURL = tab.url;
});
I don not understand, the code above can be used in background page to get the current tab's url.