FirefoxOS: return array using device storage api - javascript

I am just getting started with coding for FirefoxOS and am trying to get a list of files in a directory.
The idea is to find the name of each file and add it to the array (which works), but I want to return the populated array and this is where I come unstuck. It seems that the array gets populated during the function (as I can get it to spit out file names from it) but when I want to return it to another function it appears to be empty?
Here is the function in question:
function getImageFromDevice (){
var imageHolder = new Array();
var pics = navigator.getDeviceStorage('pictures');
// Let's browse all the images available
var cursor = pics.enumerate();
var imageList = new Array();
var count = 0;
cursor.onsuccess = function () {
var file = this.result;
console.log("File found: " + file.name);
count = count +1;
// Once we found a file we check if there are other results
if (!this.done) {
imageHolder[count] = file.name;
// Then we move to the next result, which call the cursor
// success with the next file as result.
this.continue();
}
console.log("file in array: "+ imageHolder[count]);
// this shows the filename
}
cursor.onerror = function () {
console.warn("No file found: " + this.error);
}
return imageHolder;
}
Thanks for your help!

Enumerating over pictures is an asynchronous call. Essentially what is happening in your code is this:
You are initiating an empty array
You are are telling firefox os to look for pictures on the device
Then in cursor.onsuccess you are telling firefox os to append to the array you have created WHEN it gets back the file. The important thing here is that this does not happen right away, it happens at some point in the future.
Then you are returning the empty array you have created. It's empty because the onsuccess function hasn't actually happened.
After some point in time the onsuccess function will be called. One way to wait until the array is full populated would be to add in a check after:
if (!this.done) {
imageHolder[count] = file.name;
this.continue();
}
else {
//do something with the fully populated array
}
But then of course your code has to go inside the getImageFromDevice function. You can also pass a callback function into the getImageFromDevice function.
See Getting a better understanding of callback functions in JavaScript

The problem is with the aSynchronous nature of the calls you are using.
You are returning (and probably using) the value of imageHolder when it's still empty - as calls to the "onsuccess" function are deferred calls, they happen later in time, whereas your function returns immediately, with the (yet empty) imageHolder value.
You should be doing in this case something along those lines:
function getImageFromDevice (callback){
...
cursor.onsuccess = function () {
...
if (!this.done) {
// next picture
imageHolder[count] = file.name;
this.continue();
} else {
// no more pictures, return with the results
console.log("operation finished:");
callback(imageHolder);
}
}
}
Or use Promises in your code to accomplish the same.
Use the above by e.g.:
getImageFromDevice(function(result) {
console.log(result.length+" pictures found!");
});

Related

Javascript - how to synchronously preload images and then callback once they are all loaded

I'm trying to preload images from an array synchronously with a callback when all are done. I can find lots of solutions on here which get me near to where I want to be but none of them work synchronously and allow the usage I require.
The code I have below works great but all are loaded async. I have another script which does the job sequentially but I can't seem to fire a callback once all the items in the array are done. This code below allows for user defined callback outside of the function which is what I need as I'd be calling this function on different arrays.
What I'm attempting to do is preload one array of images, check a timestamp comparison previously stored in a variable to see whether the network is slow or not, then (if the network is slow) preload a second array of images using the same preload function when the first array is done, then fire my image slideshow function once the second array is done. This is to avoid starting my image slideshow before enough images are initially loaded - to avoid blank slides, if that makes sense.
Code which works but only asynchronously:
function preload(arr){
var newimages=[], loadedImages = 0;
var postaction=function(){}
var arr=(typeof arr!="object")? [arr] : arr;
function imageloadpost(){
loadedImages++;
if (loadedImages==arr.length){
postaction(newimages); //call postaction and pass in newimages array as parameter
}
}
for (var i=0; i<arr.length; i++){
newimages[i]=new Image();
newimages[i].src=arr[i];
newimages[i].onload=function(){
imageloadpost();
}
newimages[i].onerror=function(){
imageloadpost();
}
}
return { //return blank object with done() method
done:function(f){
postaction=f || postaction; //remember user defined callback functions to be called when images load
}
}
}
Sample result I want would be:
Image 1...Loaded, then...
Image 2...Loaded, then...
Image 3...Loaded, then...
Callback: all are loaded - do something else
UPDATE:
This is the synchronous code that waits for the first image to load before firing the function again for the second and third etc etc. This is the behaviour I want but can't seem to get a callback to work when all items in the array are loaded.
function preload(arrayOfImages, index) {
index = index || 0;
if (arrayOfImages && arrayOfImages.length && arrayOfImages.length > index) {
var img = new Image();
img.onload = function() {
preload(arrayOfImages, index + 1);
};
img.onerror = function() {
preload(arrayOfImages, index + 1);
};
img.src = arrayOfImages[index];
}
$j('#slide-' + index + '').addClass('loaded');
}

how to use jQuerys each with checking an image exists

I have some source that actually drives me crazy...
First, I have a object like this:
var extensions = {"jpg":".jpg","JPG":".JPG","png":".png","PNG":".PNG"};
Now I want to iterate the object to check if a image with one of these extensions exists. The source for this looks like:
var imgURL = "some/path/";
var imgName = "myFileName";
var counter = 0;
$.each(extensions, function( key, imgExtension ){
var tmpImgUrl = imgURL + imgName + imgExtension;
console.log('test');
$( ".imageCheck" ).unbind().attr( "src", tmpImgUrl ).error( function(){
console.log( tmpImgUrl );
counter++;
if( counter >= Object.keys( extensions ).length){
return false;
}
});
});
This each should take the image name and try to test if there is an error for each extension. Actually it does what it should do. Only thing is, counter doesn't gets counted up and the console log appears wrong.
I expact the log should look like:
test
some/path/myFileName.jpg
test
some/path/myFileName.JPG
test...
and so on. But it appers like:
test
test
test
test
some/path/myFileName.jpg
some/path/myFileName.JPG
...
actually I want to use the image check to return a break (false) for the each so not all extensions get checked. (performance)
And also I really need the counter when exiting the check part....
Any guesses what going wrong here ?
Create a loader to do the image test.
Init the index to test.
Create an <img>, add onload and onerror behavior. onload will notify the user, or you can event let the loader accept callbacks for onload to call it, onerror will try to load image with next type.
The tryLoad function first check if index is equal to extensions.length, if it is, which means all possibles are failed, notify user the image load failed, and do some fail fallback or something.
Otherwise, it use current index with give fileName and url to create imagepath, set it to img.src and increase the index for next attempts.
call the tryload to start the process.
Now the image stops attempt when current url is valid, and will try the extesions one-by-one until no other can be used.
// Inits, can also be put into the loader.
var extensions = ['.jpg', '.JPG', '.png', '.PNG'];
var imgUrl = "some/path/";
var loader = function(fileName, success, fail) {
// Start at first image type.
var index = 0;
// Create a img for loading.
var $image = $('<img>');
// Add success and fail behavior to the image.
$image
.load(function() {
// Do some notification or....
console.log('success', $image.attr('src'));
// If you give a success callback, call it
// Or you can write the logic here.
if ($.isFunction(success)) {
success();
}
})
.error(function() {
console.log('fail', $image.attr('src'));
// Try to load next image
tryLoad(index);
});
// The function for attempts.
var tryLoad = function() {
// When all attemps tried.
if (index === extensions.length) {
alert('Boom, all failed');
// Do some fallbacks....
// $image.remove();
// If you give a fail callback, call it
// Or you can write the logic here.
if ($.isFunction(fail)) {
fail();
}
return;
}
// Create url to load.
var tmpImgUrl = imgURL + fileName + extensions[index];
console.log('test', tmpImgUrl);
$image.attr('src', tmpImgUrl);
++index;
};
// Start the first attempt.
tryLoad();
// return jquery wrapper of image. It'll have image only if any of the test is passed.
return $image;
};
You won't want to use jQuery's .each to loop through an object - the proper way to do this is using javascript's for in loop.
Looping through your extensions object should look like this:
for(var key in extensions){
console.log("Key", key, "Value",extensions.key);
}
You can then do whatever it is you need with those keys or values using the above looping construct.

promise with loop and file read in nodejs

I looked at lot of example but couldn't achieve it..so need help..
Problem..
the content from loop should be passed to execute one by one.
each loop iteration contains a file read and database save operation along with few other object properties that need to be assigned.
I have created example here..
http://runnable.com/VI1efZDJvlQ75mlW/api-promise-loop-for-node-js-and-hello-world
how to run:
Api: http://web-91b5a8f5-67af-4ffd-9a32-54a50b10fce3.runnable.com/api/upload
method : POST
content-type : multipart/form-data
upload more than one file with name.
..
the final expected promise is
files.name = "name of file"
files.content
files.content-type
files.size
- saved to db.
currently i am getting different content from file..but other files content are not filled and is undefined.
Regards
Moyeen
The technique you're looking for is thenable chaining
var p= Q();
Object.keys(files).forEach(function(key){
p = p.then(function(){ // chain the next one
return Q.nfcall(fs.readFile, files[key].path, "binary", i). // readfile
then(function (content) { // process content and save
files.filename = files[key].name;
files.path = files[key].path;
files.content_type = files[key].type;
files.size = files[key].size;
console.log(files.filename);
files.content = binaryToBase64(content);
return Q.npost(art.save, art); // wait for save, update as needed
}));
});
});
Basically, we tell each operation to happen after the previous one has finished by chaining them and returning which causes a wait on the asynchronous value.
As a byproduct you can later use
p.then(function(last){
// all done, access last here
});
The handler will run when all the promises are done.
I have updated the code with Q.all as the mentioned p.then will execute only once.
http://runnable.com/VI1efZDJvlQ75mlW/api-promise-loop-for-node-js-and-hello-world
form.parse(req, function(err, fields, files) {
var p = Q();
Object.keys(files).forEach(function (key) {
promises.push(p.then(function () { // chain the next one
return Q.nfcall(fs.readFile, files[key].path, "binary"). // readfile
then(function (content) { // process content and save
file = {};
file.filename = files[key].name;
file.path = files[key].path;
file.content_type = files[key].type;
file.size = files[key].size;
console.log(files[key].name);
file.content = binaryToBase64(content);
filesarr.push(file);
// Q.npost(art.save, art); // wait for save, update as needed
})
}));
Q.all(promises);
});
});
the question is how to use q.npost if i have mongoose model files and want to save...?

Adobe Air with Javascript, making a program wait while running nativeProcess

I'm trying to extract metadata from video files by running ffprobe using nativeProcess. The code below works just as it should for one file, but causes an error when trying to loop through a series of files.
I know the cause of the problem is that Air tries to start a new nativeProcess before the old one is finished. I know it's something to do with listening to the air.NativeProcessExitEvent.EXIT. I just can't get it to work. Any suggestions would be greatly appreciated.
function fileOpen(){
var directory = air.File.userDirectory;
try
{
directory.browseForDirectory("Select Directory");
directory.addEventListener(air.Event.SELECT, directorySelected);
}
catch (error)
{
air.trace("Failed:", error.message)
}
function directorySelected(event)
{
directory = event.target ;
var files = directory.getDirectoryListing();
for(i=0; i < files.length; i++){
getMetadata(files[0].nativePath)
//wait here for nativeProcess to finish
}
}
}
function getMetadata(filePathIn){
if(air.NativeProcess.isSupported)
{
}
else
{
air.trace("NativeProcess not supported.");
}
fileIn = filePathIn.toString()
var nativeProcessStartupInfo = new air.NativeProcessStartupInfo();
var file = air.File.applicationStorageDirectory.resolvePath("ffprobe");
nativeProcessStartupInfo.executable = file;
args = new air.Vector["<String>"]();
args.push("-sexagesimal","-show_format","-loglevel","quiet","-show_streams","-print_format","json",filePathIn)
nativeProcessStartupInfo.arguments = args;
process = new air.NativeProcess();
process.start(nativeProcessStartupInfo);
process.addEventListener(air.ProgressEvent.STANDARD_OUTPUT_DATA, onOutputData);
process.addEventListener(air.ProgressEvent.STANDARD_ERROR_DATA, onErrorData);
process.addEventListener(air.NativeProcessExitEvent.EXIT, onExit);
process.addEventListener(air.IOErrorEvent.STANDARD_OUTPUT_IO_ERROR, onIOError);
process.addEventListener(air.IOErrorEvent.STANDARD_ERROR_IO_ERROR, onIOError);
}
function onOutputData()
{
var fileMetadataJSON = process.standardOutput.readUTFBytes(process.standardOutput.bytesAvailable);
air.trace(fileMetadataJSON)
}
function onErrorData(event)
{
air.trace("ERROR -", process.standardError.readUTFBytes(process.standardError.bytesAvailable));
}
function onExit(event)
{
air.trace("Process exited with ", event.exitCode);
}
function onIOError(event)
{
air.trace(event.toString());
}
Here an outline that should give you an idea of what to do:
In your directorySelected() method, promote the local variable files (an Array) to a member variable. This will make the list of files that was selected available so that other methods can access it.
Remove the for loop in your directorySelected() method. Instead of running the loop and trying to run the native process here, call a new method, let's call it dequeueFileAndRunNativeProcess().
This new dequeueFileAndRunNativeProcess() method should check the length of the files array, and if it is greater than 0, it should pop() the first file in the files array and execute the native process on it.
In your onExit() method, call the dequeueFileAndRunNativeProcess() method again.
The idea here is to move the code that runs the native process into a method that you can trigger from two places: once immediately after the user finishes browsing for files, and then again after the native process finishes it's work. The process ends when the files array has no more files in it.

Variable scope. Use a closure?

I'm trying to grab all the URLs of my Facebook photos.
I first load the "albums" array with the album id's.
Then I loop through the albums and load the "pictures" array with the photos URLs.
(I see this in Chrome's JS debugger).
But when the code gets to the last statement ("return pictures"), "pictures" is empty.
How should I fix this?
I sense that I should use a closure, but not entirely sure how to best do that.
Thanks.
function getMyPhotos() {
FB.api('/me/albums', function(response) {
var data = response.data;
var albums = [];
var link;
var pictures = [];
// get selected albums id's
$.each(data, function(key, value) {
if ((value.name == 'Wall Photos')) {
albums.push(value.id);
}
});
console.log('albums');
console.log(albums);
// get the photos from those albums
$.each(albums, function(key, value) {
FB.api('/' + value + '/photos', function(resp) {
$.each(resp.data, function(k, val) {
link = val.images[3].source;
pictures.push(link);
});
});
});
console.log('pictures');
console.log(pictures);
return pictures;
});
}
You're thinking about your problem procedurally. However, this logic fails anytime you work with asynchronous requests. I expect what you originally tried to do looked something like this:
var pictures = getMyPhotos();
for (var i = 0; i < pictures.length; i++) {
// do something with each picture
}
But, that doesn't work since the value of 'pictures' is actually undefined (which is the default return type of any function without an actual return defined -- which is what your getMyPhotos does)
Instead, you want to do something like this:
function getMyPhotos(callback) {
FB.api('/me/albums', function (response) {
// process respose data to get a list of pictures, as you have already
// shown in your example
// instead of 'returning' pictures,
// we just call the method that should handle the result
callback(pictures);
});
}
// This is the function that actually does the work with your pictures
function oncePhotosReceived(pictures){
for (var i = 0; i < pictures.length; i++) {
// do something with each picture
}
};
// Request the picture data, and give it oncePhotosReceived as a callback.
// This basically lets you say 'hey, once I get my data back, call this function'
getMyPhotos(oncePhotosReceived);
I highly recommend you scrounge around SO for more questions/answers about AJAX callbacks and asynchronous JavaScript programming.
EDIT:
If you want to keep the result of the FB api call handy for other code to use, you can set the return value onto a 'global' variable in the window:
function getMyPhotos(callback) {
FB.api('/me/albums', function (response) {
// process respose data to get a list of pictures, as you have already
// shown in your example
// instead of 'returning' pictures,
// we just call the method that should handle the result
window.pictures = pictures;
});
}
You can now use the global variable 'pictures' (or, explicitly using window.pictures) anywhere you want. The catch, of course, being that you have to call getMyPhotos first, and wait for the response to complete before they are available. No need for localStorage.
As mentioned in the comments, asynchronous code is like Hotel California - you can check any time you like but you can never leave.
Have you noticed how the FB.api does not return a value
//This is NOT how it works:
var result = FB.api('me/albums')
but instead receives a continuation function and passes its results on to it?
FB.api('me/albums', function(result){
Turns out you need to have a similar arrangement for your getMyPhotos function:
function getMyPhotos(onPhotos){
//fetches the photos and calls onPhotos with the
// result when done
FB.api('my/pictures', function(response){
var pictures = //yada yada
onPhotos(pictures);
});
}
Of course, the continuation-passing style is contagious so you now need to call
getMyPhotos(function(pictures){
instead of
var pictures = getMyPhotos();

Categories