I have the following code:
decodeFile = function(theFile, arrayBuffer) {
var song = new FileDecoder().decodeFile(theFile.type, arrayBuffer);
if (song !== null) {
fileList.push(song);
}
},
fileCreated = function(file) {
var reader = new FileReader();
reader.onload = function(e) {
decodeFile(file, e.target.result);
};
reader.readAsArrayBuffer(file);
},
readDirItems = function(items) {
var i, item,
length = items.length;
for(i = 0; i < length; i++) {
item = items[i];
if (item.isFile) {
item.file(fileCreated, errorCallback);
}
}
};
//init
uploadInput.addEventListener("click", function() {
chrome.fileSystem.chooseEntry({type: "openDirectory"}, function(chosenDir) {
var dirReader,
getFilesInDirectory = function() {
dirReader.readEntries(readDirItems, errorCallback);
};
if (chosenDir && chosenDir.isDirectory) {
dirReader = chosenDir.createReader();
getFilesInDirectory();
}
});
});
How I expected this to work is that in readDirItems I would loop though all the items and for each fileEntry call the file method and enter the fileCreated callback. However this only happens for the first file. If I breakpoint in fileCreated it is only entered once. Can anyone explain what I am doing wrong?
Works for me. Your code was quite a bit more complicated than necessary for this question, so here's a simpler version. It's possible that the bug was fixed in the process of cleaning up the code.
manifest.json
{
"name": "20184022",
"description": "Answer to Stack Overflow question 20184022.",
"manifest_version": 2,
"minimum_chrome_version": "31",
"version": "0.0.0.1",
"app": {
"background": {
"scripts": ["main.js"]
}
},
"permissions": [
{"fileSystem": ["directory"]}
]
}
main.js
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('main.html', {});
});
main.html
<!DOCTYPE html>
<html>
<head><script src="iterate.js"></script></head>
<body><button id="iterate">Iterate</button></body>
</html>
iterate.js
window.onload = function() {
var errorCallback = function(e) {
console.log(e);
}
document.querySelector("#iterate").addEventListener("click", function() {
chrome.fileSystem.chooseEntry({type: "openDirectory"}, function(dir) {
if (dir && dir.isDirectory) {
var reader = dir.createReader();
reader.readEntries(function(entries) {
for (var i = 0; i < entries.length; ++i) {
console.log("entry is " + entries[i].fullPath);
}
}, errorCallback);
}
});
});
};
... and a sample run:
entry is /20184022/manifest.json iterate.js:13
entry is /20184022/iterate.js iterate.js:13
entry is /20184022/main.js iterate.js:13
entry is /20184022/main.html iterate.js:13
I recommend that you gradually change your code to match this sample, then once your code changes from broken to fixed, determine what the problem was and update your question to point out the error.
Related
I have been looking around for days now :( so really needs your help.
what i am developing is an application that downloads images and videos and display them in full screen.
for the images all is good.
But when playing videos a got this image some times and the video doesn't play.
This my code for downloading:
download(URL, fileName, callback) {
var folderName = "serverMediaDir";
var uri = encodeURI(URL);
window.requestFileSystem(
LocalFileSystem.PERSISTENT,
0,
function (fileSystem) {
var directoryEntry = fileSystem.root; // to get root path of directory
directoryEntry.getDirectory(
folderName,
{
create: true,
exclusive: false
},
function (parent) {
// Directory created successfuly
var filename = fileSystem.root.toURL() + folderName + "/" + fileName;
console.log("Directory created successfuly: " + filename);
var fileTransfer = new FileTransfer();
fileTransfer.download(
uri,
filename,
function (entry) {
// download success
var path = entry.toURL(); //**THIS IS WHAT I NEED**
console.log("Download Completed: " + entry.fullPath);
callback(null, filename, entry);
},
function (error) {
callback(error, null);
} // irrelevant download error
);
},
function (error) {
//Error while creating directory
callback(error, null);
}
);
}, function (error) {
callback(error, null);
} // irrelevant request fileSystem error
);
}
and after that i save the videos and images full paths into an array that i use to display them.
This is how i play them:
vid: function () {
var self = this;
//Accepts any number of ‘src‘ to a same video ('.mp4', '.ogg' or '.webm')
var el = document.createElement("video");
// var source = document.createElement("source");
/*for (var i = 0; i < arguments.length; i++) {
source.src = arguments[i];
// source.type = "video/" + arguments[i].split(".")[arguments[i].split(".").length - 1];
el.appendChild(source);
}*/
el.src = arguments[0];
el.onplay = function () {
clearInterval(window.sliding);
};
el.onended = function () {
window.sliding = setInterval(self.rotateImages, self.mediaUnitDuration * 1000);
self.rotateImages();
};
return el;
},
rotateMedias: function () {
var self = this;
if (self.newMedia) {
self.galleryArray = [];
if (self.mediaServer.length === 0) {
self.galleryArray.push(self.img(require(window.display.toUpperCase() ==='H'? '~/assets/H.jpg':'~/assets/V.jpg')))
}
for (var i = 0; i < self.mediaServer.length; i++) {
if (self.mediaServer[i].type.toLowerCase() === "video" && self.mediaServer[i].status) {
var obj = {};
obj = self.vid(self.mediaServer[i].path);
} else if (self.mediaServer[i].type.toLowerCase() === "image" && self.mediaServer[i].status) {
var obj = {};
obj = self.img(self.mediaServer[i].path);
}
self.galleryArray.push(obj);
}
self.newMedia = false;
}
$("#slideShow").fadeOut("slow");
setTimeout(function () {
self.curImg = self.curImg < self.galleryArray.length - 1 ? self.curImg + 1 : 0;
document.getElementById("slideShow").innerHTML = "";
self.galleryArray[self.curImg].style.width = "100%";
self.galleryArray[self.curImg].style.height = "100%";
self.galleryArray[self.curImg].style.margin = "0px";
document.getElementById("slideShow").appendChild(self.galleryArray[self.curImg]);
if (self.galleryArray[self.curImg].tagName.toLowerCase() === "video") {
self.galleryArray[self.curImg].play();
}
$("#slideShow").fadeIn("slow");
}, 500);
}
What i did try as a solution is to change the dir in which i store the files (cordova.file.applicationDirectory|dataDirectory|externalApplicationStorageDirectory...) and tried them public and private also but not working also.
Also i tried both protocol file:/// and cdvfile://localhost
I use :
cordova 10.0.0
cordova-plugin-file-transfer 1.7.1
cordova-plugin-file 6.0.2
And runing the app on X96mini box with android 7.1.2
After a lot of testing i find that it is a box bug (the application work on many others android devices with different versions). Thnx god, i found this plugin so i use it and it works fine with some work arounds for sure.
I'm trying to make the creation of these event handlers in this CodePen dynamic.
There are two viewers, and each has a handler. What if I want 4 viewers? I have to repeat the code over and over.
So instead of doing this:
var viewer1Leading = false;
var viewer2Leading = false;
var viewer1Handler = function() {
if (viewer2Leading) {
return;
}
viewer1Leading = true;
viewer2.viewport.zoomTo(viewer1.viewport.getZoom());
viewer2.viewport.panTo(viewer1.viewport.getCenter());
viewer1Leading = false;
};
var viewer2Handler = function() {
if (viewer1Leading) {
return;
}
viewer2Leading = true;
viewer1.viewport.zoomTo(viewer2.viewport.getZoom());
viewer1.viewport.panTo(viewer2.viewport.getCenter());
viewer2Leading = false;
};
viewer1.addHandler('zoom', viewer1Handler);
viewer2.addHandler('zoom', viewer2Handler);
viewer1.addHandler('pan', viewer1Handler);
viewer2.addHandler('pan', viewer2Handler);
I want to be able to sort of do that:
// Our array of viewers
let objArray = [createViewerObj(viewer1), createViewerObj(viewer2)];
// We need a viewer handler for each viewer
let viewerHandler = function (activeViewer, objArray) {
let theOthers = [];
for (let i = 0; i < objArray.length; i++) {
if (activeViewer.id !== objArray[i].id) {
theOthers.push(objArray[i]);
}
}
// OK, now I have an array of everything but the "active" viewer.
for (let i = 0; i < theOthers.length; i++) {
// If any other viewer is leading, return.
if (theOthers[i].isLeading) {
return;
}
}
activeViewer.isLeading = true; // Turn on.
objArray.forEach(function (obj) {
// Set viewport for all viewers
obj.viewer.viewport.zoomTo(activeViewer.viewport.getZoom());
obj.viewer.viewport.panTo(activeViewer.viewport.getCenter());
});
activeViewer.isLeading = false; // Turn off.
};
// Add handlers
objArray.forEach(function (obj) {
// When Viewer i is zooming we want Viewer i to lead
obj.viewer.addHandler('zoom', viewerHandler(obj.viewer, objArray));
// When Viewer i is panning we want Viewer i to lead
obj.viewer.addHandler('pan', viewerHandler(obj.viewer, objArray));
});
// Create obj with props
function createViewerObj(viewer) {
return {
id: viewer.id,
isLeading: false,
viewer: viewer
}
}
But it's not working - the viewers are no longer synchronized.
Also I wanted to make the code less clunky, but now it's more clunky.
Any ideas?
Modified CodePen here.
var globalConfig = {
Image: {
xmlns: "http://schemas.microsoft.com/deepzoom/2008",
Url: "//openseadragon.github.io/example-images/duomo/duomo_files/",
Format: "jpg",
Overlap: "2",
TileSize: "256",
Size: {
Width: "13920",
Height: "10200"
}
}
};
function createSyncedViews(imageSrc, ids) {
var syncedViews = []
var activeViewer = null; //magic init
ids.forEach(function (id) {
var currentViewer = OpenSeadragon({
id: id,
prefixUrl: imageSrc,
tileSources: globalConfig
})
currentViewer.addHandler('pan', handler);
currentViewer.addHandler('zoom', handler);
function handler() {
console.log(id, activeViewer);
//this magic is required to skip few initial events triggering while image is loading or whatever
//without it, this funky DraagonView will end up in stackoverlow
if (activeViewer == null)activeViewer = id;
if (activeViewer != id)return;
//end magic
syncedViews.forEach(function (view) {
if (view.id == id) {
return
}
view.viewport.zoomTo(currentViewer.viewport.getZoom());
view.viewport.panTo(currentViewer.viewport.getCenter());
})
//magic support
activeViewer = null;
}
syncedViews.push(currentViewer)
})
}
createSyncedViews("//openseadragon.github.io/openseadragon/images/", ['viewer1', 'viewer2','viewer3'])
try this
this is my way
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Synchronized Viewers</title>
<style>
.seadragon-viewer {
display: inline-block;
width: 45%;
height: 600px;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="//openseadragon.github.io/openseadragon/openseadragon.min.js"></script>
<script>
// Define our image
var duomo = {
Image: {
xmlns: "http://schemas.microsoft.com/deepzoom/2008",
Url: "//openseadragon.github.io/example-images/duomo/duomo_files/",
Format: "jpg",
Overlap: "2",
TileSize: "256",
Size: {
Width: "13920",
Height: "10200"
}
}
};
// Create osd viewer
function createViewer(id, tileSources) {
return OpenSeadragon({
id: id,
prefixUrl: "//openseadragon.github.io/openseadragon/images/",
tileSources: tileSources
});
}
// Create obj with props
function createViewerObj(viewer) {
return {
id: viewer.id,
isLeading: false,
viewer: viewer
}
}
// Now create the viewers
$(function(){
function dynamic_createView(arrViews){
var htmlContent="";
for (var i = 0; i < arrViews.length; i++) {
htmlContent+='<div id="'+arrViews[i]+'" class="seadragon-viewer"></div>';
}
$("#mainView")[0].innerHTML=htmlContent;
for (var i = 0; i < arrViews.length; i++) {
window[arrViews[i]]= createViewer(arrViews[i],duomo);
}
var objArray= [];
for (var i = 0; i < arrViews.length; i++) {
objArray[i]=createViewerObj(window[arrViews[i]]);
}
// We need a viewer handler for each viewer
var viewerHandler = function (activeViewer, objArray) {
var theOthers = [];
for (var i = 0; i < objArray.length; i++) {
if (activeViewer.id !== objArray[i].id) {
theOthers.push(objArray[i]);
}
}
// OK, now I have an array of everything but the "active" viewer.
for (var i = 0; i < theOthers.length; i++) {
// If any other viewer is leading, return.
if (theOthers[i].isLeading) {
return;
}
}
activeViewer.isLeading = true; // Turn on.
objArray.forEach(function (obj) {
// Set viewport for all viewers
obj.viewer.viewport.zoomTo(activeViewer.viewport.getZoom());
obj.viewer.viewport.panTo(activeViewer.viewport.getCenter());
});
activeViewer.isLeading = false; // Turn off.
};
// Add handlers
objArray.forEach(function (obj) {
// When Viewer i is zooming we want Viewer i to lead
obj.viewer.addHandler('zoom', viewerHandler(obj.viewer, objArray));
// When Viewer i is panning we want Viewer i to lead
obj.viewer.addHandler('pan', viewerHandler(obj.viewer, objArray));
});
}
dynamic_createView(["viewer1","viewer2","viewer3"]);
});
</script>
</head>
<body>
<div id="mainView"></div>
</body>
</html>
I have been searching the net and testing for hours now but I could not pinpoint what the error is. So, I am looking for your kind help here. I followed step by step multiple files upload tutorial with the drag & drop functionality but I got the error message as mentioned in the title (and the line of code that throws the error is xmlhttp.send(data); ).
File upload.js has this function:
(function(o) {
"use strict";
var ajax, getFormData, setProgress;
ajax = function(data) {
var xmlhttp = new XMLHttpRequest();
var uploaded;
xmlhttp.addEventListener('readystatechange', function() {
if (this.readyState === 4) {
if(this.status === 200){
uploaded = JSON.parse(this.response);
if(typeof o.options.finished === 'function'){
o.options.finished(uploaded);
}
} else {
if(typeof o.options.error === 'function'){
o.options.error();
}
}
}
});
xmlhttp.open('post', o.options.processor);
xmlhttp.send(data);
};
getFormData = function(source) {
var data = new FormData();
var i;
for(i = 0; i < source.length; i = i + 1) {
data.append('files[]', source[i]);
}
return data;
};
o.uploader = function(options) {
o.options = options;
if(o.options.files !== undefined) {
ajax(getFormData(o.options.files));
}
};
}());
and the global.js file has this code:
(function() {
"use strict";
var dropZone = document.getElementById('drop-zone');
var uploadsFinished = document.getElementById('uploads-finished');
var startUpload = function(files) {
app.uploader({
files: files,
Processor: 'upload.php',
finished: function(data){
var x;
var uploadedElement;
var uploadedAnchor;
var uploadStatus;
for (x = 0; x < data.length; x = x + 1) {
currFile = data[x];
uploadedElement = document.createElement('div');
uploadedElement.className = 'uploaded-console-upload';
uploadedAnchor = document.getElementById('a');
uploadedAnchor.textContent = currFile.name;
if(currFile.uploaded) {
uploadedAnchor.href = 'uploads/' + currFile.file;
}
uploadedStatus = document.createElement('span');
uploadedStatus.textContent = currFile.uploaded ? 'uploaded' : 'Failed';
uploadedElement.appendChild(uploadedAnchor);
uploadedElement.appendChild(uploadedStatus);
uploadsFinished.appendChild(uploadedElement);
}
uploadsFinished.className = '';
},
error: function() {
//console.log('There was an error');
}
});
};
//Standard form upload
document.getElementById('standard-upload').addEventListener('click', function(e) {
var standardUploadFiles = document.getElementById('standard-upload-files').files;
e.preventDefault();
startUpload();
});
//Drop funtionality
dropZone.ondrop = function(e){
e.preventDefault();
this.className = 'upload-console-drop';
startUpload(e.dataTransfer.files);
};
dropZone.ondragover = function() {
this.className = 'upload-console-drop drop';
return false;
};
dropZone.ondragleave = function() {
this.className = 'upload-console-drop';
return false;
};
}());
Any help to solve this problem would be greatly appreciated. Thank you.
o.options.processor was defined as Processor and when it was called back later in the code, it was referred to as processor (changing from Processor to processor) solved the case. Thank you guys with your comments that helped me to find the error.
Recently I want to start a project by piggyback someone's extension. I want to scope one of the image source (local variable, a base64 url) and then photo recognize it on the popup page. I keep getting error "imgb64.replace is not a function" or "imgb64" not defined.
like my title said, I want to scope the local variable in.in.inside a function (from backgound.js )and use it globally (or in popup.js). very new to this, please help guys.
// this is popup.js
chrome.runtime.getBackgroundPage(function(bg) {
bg.capture(window);
});
/// what I did
function img_find() {
var imgs = document.getElementsByTagName("img");
var imgSrcs = [];
for (var i = 0; i < imgs.length; i++) {
imgSrcs.push(imgs[i].src);
}
return imgSrcs;
}
var imgb64 = img_find();
try {
const app = new Clarifai.App({
apiKey: 'mykey'
});
}
catch(err) {
alert("Need a valid API Key!");
throw "Invalid API Key";
}
// Checks for valid image type
function validFile(imageName) {
var lowerImageName = imageName.toLowerCase();
return lowerImageName.search(/jpg|png|bmp|tiff/gi) != -1;
}
var imageDetails = imgb64.replace(/^data:image\/(.*);base64,/, '');
console.log(imageDetails)
app.models.predict("e466caa0619f444ab97497640cefc4dc", {base64:
imageDetails}).then(
function(response) {
// do something with response
},
function(err) {
// there was an error
}
);
/// end what I did
below is background.js, I think what I need is the local var img.src, thats all.
function capture(popup) {
function callOnLoad(func) {
popup.addEventListener("load", func);
if (popup.document.readyState === "complete") {
func();
}
}
crxCS.insert(null, { file: "capture.js" }, function() {
crxCS.callA(null, "get", function(result) {
var scrShot, zm, bufCav, bufCavCtx;
function mkImgList() {
for (var i = 0; i < result.vidShots.length; i++) {
var img = new popup.Image();
img.onload = function() {
this.style.height = this.naturalHeight /
(this.naturalWidth / 400) + "px";
};
if (result.vidShots[i].constructor === String) {
img.src = result.vidShots[i];
} else {
bufCav.width = result.vidShots[i].width * zm;
bufCav.height = result.vidShots[i].height * zm;
bufCavCtx.drawImage(scrShot, -result.vidShots[i].left *
zm, -result.vidShots[i].top * zm);
img.src = bufCav.toDataURL('image/png');
////// maybe clarifai here ?
////end clarifai
}
popup.document.body.appendChild(img);
}
popup.onclick = function(mouseEvent) {
if (mouseEvent.target.tagName === "IMG") {
chrome.downloads.download({ url: mouseEvent.target.src,
saveAs: true, filename: "chrome_video_capture_" + (new Date()).getTime() +
".png" });
}
};
popup.onunload = function(mouseEvent) {
crxCS.callA(null, "rcy");
};
} /// end mkImgList
if (result.needScrShot) {
bufCav = popup.document.createElement("canvas");
bufCavCtx = bufCav.getContext("2d");
chrome.tabs.captureVisibleTab({ format: "png" },
function(dataUrl) {
scrShot = new Image();
scrShot.onload = function() {
chrome.tabs.getZoom(function(zoomFactor) {
zm = zoomFactor;
callOnLoad(function() {
mkImgList(zoomFactor);
});
});
};
scrShot.src = dataUrl;
});
} else if (result.vidShots.length) {
callOnLoad(mkImgList);
} else {
popup.document.body.appendChild(notFound);
}
}); // end crxCS.callA
}); // end crxCS.insert
} // end capture
Please help guys. :)
i am developing chrome extension and i want to set the download location for the downloadable files. So i am using chrome.downloads.download API saveAs:true.It is working fine in windows OS but in Mac OS saveAs popup is flashing on the screen and then extension popup and saveAs dialogue is closing before i see them.
Any idea?
My updated code:
manifest.json
{
"name": "Download Selected Links",
"description": "Select links on a page and download them.",
"version": "0.1",
"minimum_chrome_version": "16.0.884",
"permissions": ["downloads", "<all_urls>"],
"background": {
"scripts": ["background.js"]
},
"browser_action": {"default_popup": "popup.html"},
"manifest_version": 2
}
popup.js
var allLinks = [];
var visibleLinks = [];
var filename = [];
var count = 0;
// Display all visible links.
function showLinks() {
var linksTable = document.getElementById('links');
while (linksTable.children.length > 1) {
linksTable.removeChild(linksTable.children[linksTable.children.length - 1])
}
for (var i = 0; i < visibleLinks.length; ++i) {
var row = document.createElement('tr');
var col0 = document.createElement('td');
var col1 = document.createElement('td');
var checkbox = document.createElement('input');
checkbox.checked = true;
checkbox.type = 'checkbox';
checkbox.id = 'check' + i;
col0.appendChild(checkbox);
col1.innerText = visibleLinks[i];
col1.style.whiteSpace = 'nowrap';
col1.onclick = function() {
checkbox.checked = !checkbox.checked;
}
row.appendChild(col0);
row.appendChild(col1);
linksTable.appendChild(row);
}
}
function toggleAll() {
var checked = document.getElementById('toggle_all').checked;
for (var i = 0; i < visibleLinks.length; ++i) {
document.getElementById('check' + i).checked = checked;
}
}
function downloadLinks() {
var urlArray = new Array();
for (var i = 0; i < visibleLinks.length; ++i) {
if (document.getElementById('check' + i).checked) {
urlArray.push(visibleLinks[i]);
}
}
var zip = new JSZip();
downloadFile(urlArray[count], onDownloadComplete, urlArray, zip);
}
// Re-filter allLinks into visibleLinks and reshow visibleLinks.
function filterLinks() {
var filterValue = document.getElementById('filter').value;
if (document.getElementById('regex').checked) {
visibleLinks = allLinks.filter(function(link) {
return link.match(filterValue);
});
} else {
var terms = filterValue.split(' ');
visibleLinks = allLinks.filter(function(link) {
for (var termI = 0; termI < terms.length; ++termI) {
var term = terms[termI];
if (term.length != 0) {
var expected = (term[0] != '-');
if (!expected) {
term = term.substr(1);
if (term.length == 0) {
continue;
}
}
var found = (-1 !== link.indexOf(term));
if (found != expected) {
return false;
}
}
}
return true;
});
}
showLinks();
}
chrome.runtime.onMessage.addListener(function(links) {
for (var index in links) {
allLinks.push(links[index]);
}
allLinks.sort();
visibleLinks = allLinks;
showLinks();
});
window.onload = function() {
document.getElementById('filter').onkeyup = filterLinks;
document.getElementById('regex').onchange = filterLinks;
document.getElementById('toggle_all').onchange = toggleAll;
document.getElementById('downloadButtonId').onclick = downloadLinks;
chrome.windows.getCurrent(function (currentWindow) {
chrome.tabs.query({active: true, windowId: currentWindow.id},
function(activeTabs) {
chrome.tabs.executeScript(
activeTabs[0].id, {file: 'source.js', allFrames: true});
});
});
};
source.js
var links = [].slice.apply(document.getElementsByTagName('a'));
links = links.map(function(element) {
var href = element.href;
var hashIndex = href.indexOf('#');
if (hashIndex >= 0) {
href = href.substr(0, hashIndex);
}
return href;
});
links.sort();
// Remove duplicates and invalid URLs.
var kBadPrefix = 'javascript';
for (var i = 0; i < links.length;) {
if (((i > 0) && (links[i] == links[i - 1])) ||
(links[i] == '') ||
(kBadPrefix == links[i].toLowerCase().substr(0, kBadPrefix.length))) {
links.splice(i, 1);
} else {
++i;
}
}
chrome.runtime.sendMessage(links);
background.js
function downloadFile(url, onSuccess, arrayOfUrl, zip) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = "blob";
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
if (onSuccess) {
onDownloadComplete(xhr.response, arrayOfUrl, zip);
}
}
}
xhr.send(null);
}
function onDownloadComplete(blobData, urls, zip){
if (count < urls.length) {
blobToBase64(blobData, function(binaryData){
// add downloaded file to zip:
var fileName = urls[count].substring(urls[count].lastIndexOf('/')+1);
// zip.file(fileName, binaryData, {base64: true});
zip.file(fileName+".docx", binaryData, {base64: true}); //file"+count+".docx"
if (count < urls.length -1){
count++;
downloadFile(urls[count], onDownloadComplete, urls, zip);
} else {
chrome.runtime.getBackgroundPage(function () {
zipAndSaveFiles(zip);
});
}
});
}
}
function blobToBase64(blob, callback) {
var reader = new FileReader();
reader.onload = function() {
var dataUrl = reader.result;
var base64 = dataUrl.split(',')[1];
callback(base64);
};
reader.readAsDataURL(blob);
}
function zipAndSaveFiles(zip) {
chrome.windows.getLastFocused(function(window) {
var content = zip.generate(zip);
var zipName = 'download.zip';
var dataURL = 'data:application/zip;base64,' + content;
chrome.downloads.download({
url: dataURL,
filename: zipName,
saveAs: true
});
});
}
This is a known bug on MAC for over 3 years now. As a work-around, you can delegate the dialog-opening-zipping-and-downloading action to your background page.
In order to achieve this, you need to make the following modification to your code (organized by file):
popup.html (or whatever you call it)
Remove JSZip (you only need it in the background-page now).
manifest.json
// Replace:
"background": {
"scripts": ["background.js"]
},
// with:
"background": {
"scripts": ["jszip.js(or whatever the name)", "background.js"]
},
background.js
// Replace:
if (onSuccess) {
onDownloadComplete(xhr.response, arrayOfUrl, zip);
}
// with:
if (onSuccess) {
onSuccess(xhr.response, arrayOfUrl, zip);
}
// Replace:
chrome.runtime.getBackgroundPage(function () {
zipAndSaveFiles(zip);
});
// with:
zipAndSaveFiles(zip);
// Add:
var count;
chrome.runtime.onMessage.addListener(function (msg) {
if ((msg.action === 'download') && (msg.urls !== undefined)) {
// You should check that `msg.urls` is a non-empty array...
count = 0;
var zip = new JSZip();
downloadFile(msg.urls[count], onDownloadComplete, msg.urls, zip);
}
}
popup.js
// Replace:
function downloadLinks() {
...
}
// with:
function downloadLinks() {
var urlArray = new Array();
for (var i = 0; i < visibleLinks.length; ++i) {
if (document.getElementById('check' + i).checked) {
urlArray.push(visibleLinks[i]);
}
}
//var zip = new JSZip();
//downloadFile(urlArray[count], onDownloadComplete, urlArray, zip);
chrome.runtime.sendMessage({
action: 'download',
urls: urlArray
});
}
(As I already mentioned, I am only guessing here, since I am not able to reproduce the issue.)