Error: Could not complete the operation due to error 800a138f - javascript

I am getting following error in IE 11 -
Error: Could not complete the operation due to error 800a138f.
Code is as follows -
File = function(k, j, i) {
var e = new Blob(k, i);
e.name = j;
e.lastModifiedDate = new Date();
return e;
}
Not sure what is going wrong. I do not see any other erros in the console. Just some subsequent error saying cannot find property of null (since above file is null).
Any idea whats wrong with above piece of code. Works fine with chrome and firefox?
EDIT :
The entire logic is -
try {
new File([], "")
} catch (g) {
console.log(g);
File = function(k, j, i) {
var e = new Blob(k, i);
e.name = j;
e.lastModifiedDate = new Date();
return e;
}
}
I added log to see why it goes to catch and I see following -
TypeError: Object doesn't support this action

Even though I don't have an IE under the hands to test it, I would say it's because window.File does exist on this browser and that you can't override this property.
Indeed, even though you are not able to call the File constructor from your script in this browser, the File constructor existed there, so e.g what you get in an input[type='file'] is an instance of File.
console.log(typeof window.File === "function" && 'File already exists');
inp.onchange = function() {
if(inp.files[0] instanceof File) {
console.log("yep, that's a File");
}
};
<input type="file" id="inp">
So they made this property non-writable, what now?
Well, that depends on what you wanted to do with this File anyway.
There is currently a very limited number of use cases for such an object:
Avoid to set the third param filename to FormData.append('fieldName', blob, filename),
and only in chrome, append the File to a FileList through DataTransfer.items.add() method.
Everything else you could have done with this File, will be done the same with a Blob, and these two methods won't be accessible to your fakeFile anyway.
But if you really want to do it, then I guess that simply choosing an other name for your function will make IE happy:
// Beware, untested code below
(function() {
var blob = new Blob(['foo'], {
type: 'text/plain'
}),
file;
try {
file = new File([blob], 'foo.txt');
if (file.name !== 'foo.txt' || !file.lastModifiedDate || !file.type === 'text/plain') {
throw new Error('invalid');
}
window.MyFile = File;
} catch (e) {
window.MyFile = function(k, j, i) {
var e = new Blob(k, i);
e.name = j;
e.lastModifiedDate = new Date();
return e;
};
}
})();
var file = new MyFile([new Blob(['bar'], {
type: 'text/plain'
})], 'mytext.txt');
console.log(file);

Related

Why I have "ArrayBuffer is not a constructor" error?

I'm writing an extension to the extension "Binary File Viewer" for VS Code. My code is
/**
* Select the 'aid' extension.
*/
registerFileType((fileExt, filePath, fileData) => {
return fileExt == 'wav';
});
registerParser(() => {
const buffer = new ArrayBuffer(4);
let temp = new DataView(buffer);
temp.setInt32(0, 123)
let I = String(temp.getFloat32());
addRow('I', I, '');
});
When I run it (actually, when it's involved by the Binary File Viewer), I have an error "Type error: ArrayBuffer is not a constructor". What I've done wrong?

readAsText(file) only reads the last file in a directory

I'm trying to read all the files in a user's directory and display their content in a text box.
Reading single files works perfectly, however, when I try to read a whole directory, things are getting weird.
While iterating through a directory, only the last file in the directory is read correctly. This behavior is consistent no matter how many files are in the directory.
Here's the code I use for reading the files:
results.forEach(function(item) {
reader = new FileReader();
// This line is reached
console.log("filename: " + item.name);
item.file(function(File) {
// This one only for the last file in that directory
reader.readAsText(File);
console.log("success");
});
// This line is reached
console.log("read: " + item.name);
});
Here's the log (from the dev tools):
filename: app.js
read: app.js
filename: main.js
read: main.js
filename: SharedPreferences.js
read: SharedPreferences.js
filename: KeyConstants.js
read: KeyConstants.js
success
If you have any questions, please ask them, I'm trying this for hours now and I'm slowly getting tired of failing over and over ..
This happens because FileReader works asynchronously, which means approximately that it starts executing a task (reading the file) while the code continues to be executed. If you want to do something with the result for each file as soon as the load is finished, you need to play with this method:
reader.onloadend = function(evt) {
// file is loaded
// do something with evt.target object
};
My final solution:
Like abcdn said, the problem was that I was overriding the reader with a new one.
I solved this by using javascript closures (which I had no idea existed because I'm coming from C#).
Here's the full code I used in the end:
chrome.fileSystem.chooseEntry({type: "openDirectory"}, function(dir) {
readFolderAsArrayBuffer(dir, function() {
console.log("read folder");
});
});
function readFolderAsArrayBuffer(dir, callback) {
if (dir && dir.isDirectory) {
var reader = dir.createReader();
var handlefile = function (entries) {
for (var i = 0; i < entries.length; i++) {
arr[i] = (function(fileEntry, number) {
console.log("returning function " + number);
entries[number].file(function(file) {
handleread(fileEntry, file);
console.log("reading" + url);
});
})(entries[i], i);
}
}
var handleerror = function() {
console.log("error");
};
reader.readEntries(handlefile, handleerror);
}
}
var handleread = function(fileEntry, file) {
var fileReader = new FileReader();
fileReader.onloadend = function(evt) {
console.log("Read file: " + fileEntry.name + "with the content: " + evt.target.result);
};
fileReader.readAsText(file);
}
This reads a whole user selected directory and outputs each file's content to the console.

AngularJs dynamic download from response

I have written a directive based on Scott's answer. You'd use it like so:
<button class="btn btn-success"
download-response="getData()"
download-success="getDataSuccess()"
download-error="getDataError()"
download-name="{{name}}.pdf"
download-backup-url="/Backup/File.pdf">
Save
</button>
Issue: the code below throws an error TypeError: Invalid calling object in IE11 on the first method (line: saveBlob(blob, filename);). Even though it falls back to other methods of downloading, it is my understanding that the saveMethod1 should work in IE11.
Here is the code:
'use strict';
// directive allows to provide a function to be executed to get data to be downloaded
// attributes:
// download-response - Required. Function to get data. It must return a promise. It must be declared on the $scope.
// download-success - Optional. Function to be executed if download-response function was successfully resolved. It must be declared on the $scope.
// download-error - Optional. Function to be executed if download-response function return a promise rejection. It must be declared on the $scope.
// download-mime - Optional. provide a mime type of data being downloaded. Defaulted to "application/octet-stream"
// download-name - Optional. name of the file to download. Defaulted to "download.bin"
// download-backup-url - in case browser does not support dynamic download, this url will be called to get the file
angular.module('app.directives')
.directive('downloadResponse', [ '$parse', '$timeout',
function ($parse, $timeout) {
function saveMethod1(data, filename, contentType) {
// Support for saveBlob method (Currently only implemented in Internet Explorer as msSaveBlob, other extension in case of future adoption)
var saveBlob = navigator.msSaveBlob || navigator.webkitSaveBlob || navigator.mozSaveBlob || navigator.saveBlob;
if (saveBlob) {
// Save blob is supported, so get the blob as it's contentType and call save.
var blob = new Blob([data], { type: contentType });
saveBlob(blob, filename);
//console.log("SaveBlob Success");
} else {
throw 'saveBlob is not supported. Falling back to the next method';
}
}
function saveMethod2(data, filename, contentType, octetStreamMime) {
// Get the blob url creator
var urlCreator = window.URL || window.webkitURL || window.mozURL || window.msURL;
if (urlCreator) {
// Try to use a download link
var link = document.createElement("a");
var url;
if ("download" in link) {
// Prepare a blob URL
var blob = new Blob([data], { type: contentType });
url = urlCreator.createObjectURL(blob);
link.setAttribute("href", url);
// Set the download attribute (Supported in Chrome 14+ / Firefox 20+)
link.setAttribute("download", filename);
// Simulate clicking the download link
var event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
link.dispatchEvent(event);
//console.log("Download link Success");
} else {
// Prepare a blob URL
// Use application/octet-stream when using window.location to force download
var blob = new Blob([data], { type: octetStreamMime });
url = urlCreator.createObjectURL(blob);
window.location = url;
//console.log("window.location Success");
}
} else {
throw 'UrlCreator not supported. Falling back to the next method';
}
}
function saveMethod3(attrs) {
if (attrs.downloadBackupUrl && attrs.downloadBackupUrl != '') {
console.log('opening ' + attrs.downloadBackupUrl);
window.open('http://' + document.domain + attrs.downloadBackupUrl, '_blank');
} else {
throw 'Could not download a file using any of the available methods. Also you did not provide a backup download link. No more bullets left...';
}
}
return {
restrict: 'A',
scope: false,
link:function (scope, elm, attrs) {
var getDataHandler = $parse(attrs.downloadResponse);
elm.on('click', function() {
var promise = getDataHandler(scope);
promise.then(
function (data) {
if (attrs.downloadSuccess && attrs.downloadSuccess != '') {
var successHandler = $parse(attrs.downloadSuccess);
successHandler(scope);
}
var octetStreamMime = "application/octet-stream";
var filename = attrs.downloadName || "download.bin";
var contentType = attrs.downloadMime || octetStreamMime;
try {
saveMethod1(data, filename, contentType);
return;
} catch (e) {
console.log(e);
try {
saveMethod2(data, filename, contentType, octetStreamMime);
return;
} catch (e) {
console.log(e);
try {
saveMethod3(attrs);
return;
} catch (e) {
throw e;
}
throw e;
}
throw e;
}
},
function(data) {
if (attrs.downloadError && attrs.downloadError != '') {
var errorHandler = $parse(attrs.downloadError);
errorHandler(scope);
}
}
);
});
}
};
}
]);
Any help is greatly appreciated!
Problem:
I have resolved this issue. It appears that Internet Explorer 11 does not like aliases to the msSaveBlob function, demonstrated by the simplest examples:
// Succeeds
navigator.msSaveBlob(new Blob(["Hello World"]), "test.txt");
// Fails with "Invalid calling object"
var saveBlob = navigator.msSaveBlob;
saveBlob(new Blob(["Hello World"]), "test.txt");
So essentially the generic alias created to encapsulate saveBlob functionality, which should be permissible, prevents msSaveBlob from working as expected:
var saveBlob = navigator.msSaveBlob || navigator.webkitSaveBlob || navigator.mozSaveBlob || navigator.saveBlob;
Workaround:
So the work around is to test for msSaveBlob separately.
if(navigator.msSaveBlob)
navigator.msSaveBlob(blob, filename);
else {
// Try using other saveBlob implementations, if available
var saveBlob = navigator.webkitSaveBlob || navigator.mozSaveBlob || navigator.saveBlob;
if(saveBlob === undefined) throw "Not supported";
saveBlob(blob, filename);
}
tl;dr
So the msSaveBlob method works, as long as you don't alias the function. Perhaps it's a security precaution - but then perhaps a security exception would have been more appropriate, though I think it is most likely a flaw, considering the source. :)

Firefox Extension/Addon : Reading a text file from remote server(URL)

After wasting my two days to find out what's going wrong with this script, finally I decide to ask it.
What I am trying to do
I am trying to read a text file from remote server. Then storing all text file updates to an SQLITE database at the time of my Firefox Extension/Addon get loaded.
What I tried
var updatereader = {
start: function () {
//alert('reading update');
var fURL = null;
var ioService = null;
var fURI = null;
var httpChannel = null;
fURL = "http://www.example.com/addon/mlist.txt";
ioService = Components.classes["#mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
fURI = ioService.newURI(fURL, null, null);
httpChannel = ioService.newChannelFromURI(fURI).QueryInterface(Components.interfaces.nsIHttpChannel);
httpChannel.asyncOpen(updatereader.StreamReader, null);
},
onUpdateCompleted: function () {
},
StreamReader:
{
fOutputStream: null,
fPointer: null,
tempFile: "mlist.txt",
onStartRequest: function (aRequest, aContext) {
//alert('onStart');
updatereader.StreamReader.fOutputStream = Components.classes["#mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
updatereader.StreamReader.fPointer = Components.classes["#mozilla.org/file/directory_service;1"].getService(Components.interfaces.nsIProperties).get("ProfD", Components.interfaces.nsIFile);
updatereader.StreamReader.fPointer.append(updatereader.StreamReader.tempFile);
updatereader.StreamReader.fOutputStream.init(updatereader.StreamReader.fPointer, 0x02 | 0x20 | 0x08, 0644, 0);
},
onDataAvailable: function (aRequest, aContext, aInputStream, aOffset, aCount) {
//control flow is not entering here - may be here is somehting missing
var sStream = null;
var tempBuffer = null;
sStream = Components.classes["#mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
sStream.init(aInputStream);
tempBuffer = sStream.read(aCount);
updatereader.StreamReader.fOutputStream.write(tempBuffer, aCount);
},
onStopRequest: function (aRequest, aContext, aStatusCode) {
//alert('onStop');
var currentDate = new Date();
if (aStatusCode == 0) {
fileInputStream = Components.classes["#mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
updatereader.StreamReader.fOutputStream.close();
fileInputStream.init(updatereader.StreamReader.fPointer, 0x01, 0, 0);
lineInputStream = fileInputStream.QueryInterface(Components.interfaces.nsILineInputStream);
//pass data to somewhere
var dbH = new dbstore();
dbH.updateData(lineInputStream);
lineInputStream.close();
updatereader.StreamReader.fPointer.remove(false);
updatereader.onUpdateCompleted();
} else {
}
}
}
}
Problem:
Getting nothing in lineInputStream which passes the read data to somewhere else for storing it.
Area of problem:
Program control flow is not entring to this section
onDataAvailable:
Not getting any error.
First of all, there doesn't really seem to be any need to read the file to the disk first (unless it is really, really big).
I'd just use XMLHttpRequest to get the file, which when run from a privileged context (e.g. add-on code, but not a website) can access any and every valid URI.
XMLHttpRequest will simplify almost everything, e.g. no more onDataAvailable, (usually) no more manual text converting, etc.
Also, no need to ever hit the disk during the transfer.
Code would look something like this:
var req = new XMLHttpRequest();
req.open("GET", "http://www.example.com/addon/mlist.txt"); // file:/// would work too, BTW
req.overrideMimeType("text/plain");
req.addEventListener("load", function() {
// Do something with req.responseText
}, false);
req.addEventListener("error", function() {
// Handle error
}, false);
req.send();
If you want to use XMLHttpRequest in a non-window, e.g. js code module or js components, then you need to first initialize a constructor. This is not required for windows, including XUL windows and by that XUL overlays.
// Add XMLHttpRequest constructor, if not already present
if (!('XMLHttpRequest' in this)) {
this.XMLHttpRequest = Components.Constructor("#mozilla.org/xmlextras/xmlhttprequest;1", "nsIXMLHttpRequest");
}
SDK users should use the request module, or net/xhr if a lower-level API is required.
PS: If you're still interested in using raw channels, here is a minimal example I coded up in a Scratchpad (to run, open a Scratchpad for a privileged location, e.g. about:newtab).
You shouldn't alert from your own implementation: alert() will spin the event loop and causes reentrant code, which is not supported in this context.
var {
classes: Cc,
interfaces: Ci,
results: Cr,
utils: Cu
} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm")
Cu.import("resource://gre/modules/Services.jsm");
var ConverterStream = Components.Constructor(
"#mozilla.org/intl/converter-input-stream;1",
"nsIConverterInputStream",
"init");
var RC = Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER;
function Listener() {
this.content = "";
}
Listener.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIStreamListener]),
onStartRequest: function(req, ctx) {
console.log("start");
},
onDataAvailable: function(req, ctx, stream, offset, count) {
console.log("data", count);
try {
var cs = new ConverterStream(stream, null /* utf-8 */, 4096, RC);
try {
var str = {};
while (cs.readString(4096, str)) {
this.content += str.value;
}
}
finally {
cs.close();
}
}
catch (ex) {
console.error("data", ex.message, ex);
}
},
onStopRequest: function(req, ctx, status) {
console.log("stop", status,
this.content.substr(0, 20), this.content.length);
}
};
var uri = Services.io.newURI("http://example.org", null, null);
Services.io.newChannelFromURI(uri).asyncOpen(new Listener(), null);

How to change emscripten browser input method from window.prompt to something more sensible?

I have a C++ function which once called consumes input from stdin. Exporting this function to javascript using emscripten causes calls to window.prompt.
Interacting with browser prompt is really tedious task. First of all you can paste only one line at time. Secondly the only way to indicate EOF is by pressing 'cancel'. Last but not least the only way (in case of my function) to make it stop asking user for input by window.prompt is by checking the checkbox preventing more prompts to pop up.
For me the best input method would be reading some blob. I know I can hack library.js but I see some problems:
Reading blob is asynchronous.
To read a blob, first you have to open a file user has to select first.
I don't really know how to prevent my function from reading this blob forever - there is no checkbox like with window.prompt and I'm not sure if spotting EOF will stop it if it didn't in window.prompt case (only checking a checkbox helped).
The best solution would be some kind of callback but I would like to see sime hints from more experienced users.
A way would be to use the Emscripten Filesystem API, for example by calling FS.init in the Module preRun function, passing a custom function as the standard input.
var Module = {
preRun: function() {
function stdin() {
// Return ASCII code of character, or null if no input
}
var stdout = null; // Keep as default
var stderr = null; // Keep as default
FS.init(stdin, stdout, stderr);
}
};
The function is quite low-level: is must deal with one character at a time. To read some data from a blob, you could do something like:
var data = new Int8Array([1,2,3,4,5]);
var blob = new Blob([array], {type: 'application/octet-binary'});
var reader = new FileReader();
var result;
reader.addEventListener("loadend", function() {
result = new Int8Array(reader.result);
});
var i = 0;
var Module = {
preRun: function() {
function stdin() {
if (if < result.byteLength {
var code = result[i];
++i;
return code;
} else {
return null;
}
}
var stdout = null; // Keep as default
var stderr = null; // Keep as default
FS.init(stdin, stdout, stderr);
}
};
Note (as you have hinted), due to the asynchronous nature of the reader, there could be a race condition: the reader must have loaded before you can expect the data at the standard input. You might need to implement some mechanism to avoid this in a real case. Depending on your exact requirements, you could make it so the Emscripten program doesn't actually call main() until you have the data:
var fileRead = false;
var initialised = false;
var result;
var array = new Int8Array([1,2,3,4,5]);
var blob = new Blob([array], {type: 'application/octet-binary'});
var reader = new FileReader();
reader.addEventListener("loadend", function() {
result = new Int8Array(reader.result);
fileRead = true;
runIfCan();
});
reader.readAsArrayBuffer(blob);
var i = 0;
var Module = {
preRun: function() {
function stdin() {
if (i < result.byteLength)
{
var code = result[i];
++i;
return code;
} else{
return null;
}
}
var stdout = null;
var stderr = null;
FS.init(stdin, stdout, stderr);
initialised = true;
runIfCan();
},
noInitialRun: true
};
function runIfCan() {
if (fileRead && initialised) {
// Module.run() doesn't seem to work here
Module.callMain();
}
}
Note: this is a version of my answer at Providing stdin to an emscripten HTML program? , but with focus on the standard input, and adding parts about passing data from a Blob.
From what I understand you could try the following:
Implement selecting a file in Javascript and access it via Javascript Blob interface.
Allocate some memory in Emscripten
var buf = Module._malloc( blob.size );
Write the content of your Blob into the returned memory location from Javascript.
Module.HEAPU8.set( new Uint8Array(blob), buf );
Pass that memory location to a second Emscripten compiled function, which then processes the file content and
Deallocate allocated memory.
Module._free( buf );
Best to read the wiki first.

Categories

Resources