Returning data from a class - javascript

I have a file drop class. User drop images on (as many as they wish) and these are then uploaded.
I call the class via from my main class:
this.fileDrop = new lx.FileDrop();
Here's the class:
(function(){
"use strict";
var FileDrop = function() {
this.init();
};
p.init = function() {
this._initEvents();
};
p._initEvents = function() {
$(window).on('drop', this.onDrop.bind(this)).on('dragover', this.onDragOver);
};
p.onDrop = function(e) {
e.preventDefault();
var self = this;
var files = e.originalEvent.dataTransfer.files;
$.each(files, function(index, file){
self.readFile(file).done(function(data) {
//how to return the data?
});
});
};
p.onDragOver = function(e) {
e.preventDefault();
};
p.readFile = function(file) {
var fileReader = new FileReader();
var deferred = $.Deferred();
fileReader.onload = function(event) {
deferred.resolve(event.target.result);
};
fileReader.onerror = function() {
deferred.reject(this);
};
fileReader.readAsDataURL(file);
return deferred.promise();
};
lx.FileDrop = FileDrop;
}(window));
My question concerns returning the image data to the main class. How can I return it? How can I store it - I wish to store everything that is returned into an array in the main class. How would this work when uploading multiple images. Would some sort of deferred work?

How about something like this:
var dfd = $.Deferred(),
images = [];
$.each(files, function(index, file){
self.readFile(file).done(function(data) {
dfd.progress(data);
images.push(data);
if(files.length === ++index)
dfd.resolve(images);
}).fail(dfd.reject);
});
Handle the deferred object where ever you like:
dfd.progress(function(file){
console.log('file successfully uploaded', file);
}).done(function(images){
console.log('all files successfully uploaded', images);
}).fail(function(){
console.log('something went wrong while uploading an image');
});
Another example:
function FileDrop(){
this.uploadCount = 0;
this.images = [];
}
FileDrop.prototype.getImages = function(){
var dfd = $.Deferred(),
size = 3,
that = this;
for(var i = 0; i < size; i++){
this.getImage(i*500, function(image){
var dummyImage = $('<img/>');
// should be 'image' from the callback in your case
that.images.push(dummyImage);
dfd.notify(dummyImage);
if(that.uploadCount === size){
dfd.resolve(that.images);
}
});
}
return dfd.promise();
};
FileDrop.prototype.getImage = function(timeout, callback){
var that = this;
setTimeout(function(){
that.uploadCount++;
callback();
}, timeout);
};
var fd = new FileDrop();
fd.getImages().progress(function(image){
console.log('progress', image);
}).done(function(imageArray){
// preferred way:
//access the array when you know it's complete in the callback
console.log('done', imageArray);
});
setTimeout(function(){
// I think this is what you asked for, however, you must make an
// assumption when the images are completed, which is a bad idea
console.log(fd.images);
}, 2000);
http://jsfiddle.net/Nm5vK/2/

Related

Multiple Base64 image within 1 array causes overriding

Let's say I want to upload 2 images to an ajax, I will send them using this format
{ "base64StringName:" : "[ {"1": "base64_1"}, {"2" : "base64_2"} ]"}
So its an object that contains an array of objects of base64 strings
To do so, I will need to create an array and inside this array, I will push json objects into it.
Here is my code for this:
<script>
var test ='';
var imageArray =[];
var imageObject ={};
$('#inputFileToLoad').on('change', function(){
imageArray.length = 0;
fileCount = this.files.length;
for(i = 0; i < fileCount; i++){
var file = document.querySelector('#inputFileToLoad').files[i];
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {
test = reader.result.split(',')[1];
console.log(test);
imageObject[i] = test;
imageArray.push(imageObject);
};
reader.onerror = function (error) {
alert('Error: ', error);
};
}
});
$('#inputFileToLoadButton').on('click', function(){
console.log(imageArray);
$.ajax({
url:"some url",
method:"POST",
data: {
"base64String": imageArray
}
,success: function () {
swal("Success!","Upload Finished!","success");
//add redirect!
},
error: function (jqXHR) {
swal("Error",jqXHR.responseText, "error");
}
});
});
</script>
However, I encounter a problem, my first object inside the array somehow gets overwritten.
it becomes
{ "base64StringName:" : "[ {"1": "base64_2"}, {"2" : "base64_2"} ]"}
Also when i printed out the first base64 encoded file at console.log(test); it is undefined, but when i printed out the second base64 encoded file, it prints the second file only.
try this:
var test = '';
var imageArray = [];
var imageObject;
$('#inputFileToLoad').on('change', function() {
imageArray.length = 0;
fileCount = this.files.length;
for (i = 0; i < fileCount; i++) {
debugger;
var file = document.querySelector('#inputFileToLoad').files[i];
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function() {
test = this.result.split(',')[1];
imageObject = {};
imageObject[index] = test;
imageArray.push(imageObject);
}.bind({
index: i
});
reader.onerror = function(error) {
alert('Error: ', error);
};
}
});

Compress image by tweaking dropzone.js

I am new to JS, for last few days I am stuck at compressing image at client side. What I want to do is, User drops bunch of images (can be more than 10) at dropzone they should be compressed using JIC and once user clicks a button to upload all the compressed files get uploaded to the server.
So far my code is able to compress and upload only if one image is dropped, but when I drop multiple images all images remain uncompressed but one. I am not sure what wrong I am doing. I have tried to follow the solutions from this post but couldn't achieve my goal. Code I am using is as follows:
Dropzone.autoDiscover=false;
var myDropZone=new Dropzone("#dropzonePreview",{
url:"/dragdrop",
autoProcessQueue:false,
acceptedFiles: 'image/*',
parallelUploads: 10,
init:function(){
this.on('addedfile', function(file){
_this = this;
////console.log("Added File");
$('#userphoto').css('color', "transparent");
EXIF.getData(file, function(){ // async call
var lat=EXIF.getTag(this,"GPSLatitude");
var lon=EXIF.getTag(this,"GPSLongitude");
geocoder.geocode( { 'latLng': temp }, function(results, status) { // another async call });
}
});
myReader2 = new FileReader(); // Reading image for compression purpose
myReader2.onload = function(event) {
console.log(file.status);
// var i = new Image();
var i = document.getElementById("source_image");
i.src = event.target.result;
i.onload = function() {
var source_image = document.getElementById('source_image');
var quality = 70;
comp = jic.compress(source_image, 70, "jpg"); // Link to function can be found at the end of code.
var editedFile = base64ToFile(comp.src, file); // same function used in mentioned stackoverflow post.
// Replace original with resized
var origFileIndex = myDropZone.files.indexOf(file);
myDropZone.files[origFileIndex] = editedFile;
editedFile.status = Dropzone.ADDED;
myDropZone.enqueueFile(editedFile);
delete source_image;
};
};
myReader2.readAsDataURL(file);
});
this.on("sending",function(file,xhr,formData){
//appending some data to formData
});
this.on("complete", function(file){
// processing like removing objects of file from drop zone
});
}
});
$('#upload').click(function(evt){ // Button that triggers uploading file
myDropZone.processQueue();
}
Link to function. Your help would be really appreciated. Thank you.
I have found this issue solution. it's work for me.
Please check
function base64ToFile(dataURI, origFile) {
var byteString, mimestring;
if(dataURI.split(',')[0].indexOf('base64') !== -1 ) {
byteString = atob(dataURI.split(',')[1]);
} else {
byteString = decodeURI(dataURI.split(',')[1]);
}
mimestring = dataURI.split(',')[0].split(':')[1].split(';')[0];
var content = new Array();
for (var i = 0; i < byteString.length; i++) {
content[i] = byteString.charCodeAt(i);
}
var newFile = new File(
[new Uint8Array(content)], origFile.name, {type: mimestring}
);
// Copy props set by the dropzone in the original file
var origProps = [
"upload", "status", "previewElement", "previewTemplate", "accepted"
];
$.each(origProps, function(i, p) {
newFile[p] = origFile[p];
});
return newFile;
}
Dropzone.autoDiscover = false;
jQuery(document).ready(function($) {
var myDropZone=new Dropzone("#dropzonePreview",{
url:"/dragdrop",
autoProcessQueue:false,
acceptedFiles: 'image/*',
parallelUploads: 10,
init:function(){
this.on("sending",function(file,xhr,formData){
});
this.on("complete", function(file){
});
}
});
myDropZone.on("addedfile", function(file) {
var reader = new FileReader();
reader.addEventListener("load", function(event) {
var origImg = new Image();
origImg.src = event.target.result;
origImg.addEventListener("load", function(event) {
comp = jic.compress(origImg, 30, "jpg");
var resizedFile = base64ToFile(comp.src, file);
var origFileIndex = myDropZone.files.indexOf(file);
myDropZone.files[origFileIndex] = resizedFile;
myDropZone.enqueueFile(resizedFile);
});
});
reader.readAsDataURL(file);
});
$('#upload').click(function(e){ // Button that triggers uploading file
e.preventDefault();
myDropZone.processQueue();
});
});

jQuery.Deferred() is not working properly

I'm trying to implement indexedDB. For that I need to use $.Deferred to get the status of database creation. But the problem is, Differed is not working as expected.
Here is the fiddle, you can find the process in the console.
And here is the code:
$(function($) {
var table = 'price';
$.when(dbConnection('cw', table)).done(function(db) {
console.log(db);
var data = [];
dbInsert(db, data, table);
});
function dbConnection(dbname, table) {
var dfd = $.Deferred();
var request = indexedDB.open(dbname);
request.onupgradeneeded = function() {
// The database did not previously exist, so create object stores and indexes.
var db = request.result;
var store = db.createObjectStore(table, {
keyPath: "id"
});
var styleCode = store.createIndex("style_code", "style_code");
var colorCode = store.createIndex("color_code", "color_code");
var size = store.createIndex("size", "size");
var price1 = store.createIndex("price1", "price1");
};
request.onsuccess = function() {
db = request.result;
console.log(request.result);
dfd.resolve(db);
return dfd.promise();
};
request.onerror = function() {
report(request.error);
console.log(request.error);
dfd.resolve(null);
return dfd.promise();
};
request.onabort = function() {
report(request.error);
console.log(request.error);
dfd.resolve(null);
return dfd.promise();
};
}
function dbInsert(db, data, table) {
var tx = db.transaction(table, "readwrite");
var store = tx.objectStore(table);
$.each(data, function(i, rows) {
var style = rows['style-code'],
color = rows['color-code'],
size = rows['size'],
price = rows['price1'];
store.put({
id: i,
style_code: style,
color_code: color,
size: size,
price1: price
});
});
tx.oncomplete = function() {
console.log('Data inserted successfully.');
};
}
})(jQuery);
Whether I'm doing anything wrong? or am I missing anything in this code?. Can anyone tell me what is wrong in this code.
You are expecting dbConnection to return a promise, but do not return anything from that function. Return the promise immediately (last line below) and not inside all the callbacks:
function dbConnection(dbname, table) {
var dfd = $.Deferred();
var request = indexedDB.open(dbname);
request.onupgradeneeded = function() {
// The database did not previously exist, so create object stores and indexes.
var db = request.result;
var store = db.createObjectStore(table, {
keyPath: "id"
});
var styleCode = store.createIndex("style_code", "style_code");
var colorCode = store.createIndex("color_code", "color_code");
var size = store.createIndex("size", "size");
var price1 = store.createIndex("price1", "price1");
};
request.onsuccess = function() {
db = request.result;
console.log(request.result);
dfd.resolve(db);
};
request.onerror = function() {
report(request.error);
console.log(request.error);
dfd.resolve(null);
};
request.onabort = function() {
report(request.error);
console.log(request.error);
dfd.resolve(null);
};
return dfd.promise();
}
Updated JSFiddle: http://jsfiddle.net/TrueBlueAussie/9kjcm49b/2/
Your various callbacks simply resolve or reject the deferred. The readonly promise returned earlier then triggers the next operation.
Note: You should probably use reject for the two error cases (unless you actually want to proceed with a null db value). e.g.
request.onerror = function() {
report(request.error);
console.log(request.error);
dfd.reject("Error occurred");
};
request.onabort = function() {
report(request.error);
console.log(request.error);
dfd.reject("Operation aborted");
};
and use like this:
$.when(dbConnection('cw', table)).done(function(db) {
console.log(db);
var data = [];
dbInsert(db, data, table);
}).fail(function(message){
alert(message);
});

Javascript use return value in another function

What I want to achive is using the return value of the "previewfile" function as an execution indicator for the "readfiles" function. But this needs to be after the "image.onload" part has been executed, since there I need returnThis to be set to true.
I've researched several things on Google and Stackoverflow concerning this problem and callbacks / deferred objects in general, but I cannot wrap my head around how to applicate that in this situation.
I have the following constellation in my Image uploading section:
function previewfile(file, tests, acceptedTypes, holder) {
var returnThis = false;
if (tests.filereader === true && acceptedTypes[file.type] === true) {
var reader = new FileReader();
reader.onload = function (event) {
var image = new Image();
image.onload = function() {
var testimage = new Image();
testimage.src = $(this).attr('src');
var widthOfImage = testimage.width;
var heightOfImage = testimage.height;
if (!checkImageDimensions(widthOfImage, heightOfImage)) {
// do stuff
} else {
returnThis = true;
}
};
image.src = event.target.result;
holder.appendChild(image);
};
reader.readAsDataURL(file);
} else {
// do other stuff
}
return returnThis;
}
function readfiles(files, tests, acceptedTypes, holder, progress) {
var uploadNow = previewfile(files[0], tests, acceptedTypes, holder);
if (uploadNow === true) {
// do stuff
}
} else {
// do other stuff
}
}
I would go with something like this
function readfiles(files, tests, acceptedTypes, holder, progress) {
previewfile(files[0], tests, acceptedTypes, holder, function(value){
if (uploadNow === true){
// do stuff
}
else {
// do other stuff
}
});
}
function previewfile(file, tests, acceptedTypes, holder, callback) {
...
callback(returnValue); //instead of return
}
As previewfile() relies on asynchronous activity, it is itself effectively asynchronous. As such, it can't reliably return a value, but it can return a promise.
As others have pointed out, previewfile() can be written to accept a callback, which would avoid the need for a promise. However, if you want a promise solution, here is one (certainly not the only one).
function previewfile(file, tests, acceptedTypes, holder) {
if(tests.filereader === true && acceptedTypes[file.type] === true) {
var reader = new FileReader(),
image;
var promise_a = $.Deferred(function(dfrd) {
reader.onload = function(event) {
image.attr('src', event.target.result).appendTo(holder);
dfrd.resolve();
};
reader.onerror = function() {
dfrd.reject('fileReader error');
};
}).promise();
var promise_b = $.Deferred(function(dfrd) {
image = $("<img/>").on('load', function() {
var widthOfImage = image.width;
var heightOfImage = image.height;
if (checkImageDimensions(widthOfImage, heightOfImage)) {
dfrd.resolve();
} else {
//do stuff
dfrd.reject('image loaded but dimensions did not check out');
}
}).error(function() {
dfrd.reject('image did not load');
});
}).promise();
reader.readAsDataURL(file);
return $.when(promise_a, promise_b);
} else {
// do other stuff
// Also return a promise here, even if no async is involved.
}
}
readfiles() can now be written as follows :
function readfiles(files, tests, acceptedTypes, holder, progress) {
return previewfile(files[0], tests, acceptedTypes, holder).then(function() {
// do stuff
}).then(null, function(reason) {
console.log(reason);// or display to the user in the DOM.
// do other stuff
});
}
The benefit of a promise-based solution is maybe not so much in handling success as managing errors. Note how a single handler reports several different types of error.
With the help of FelixKling and kallehj, this is the working solution (with callback):
// important
function previewfile(file, tests, acceptedTypes, holder, callback) {
var returnThis = false;
if (tests.filereader === true && acceptedTypes[file.type] === true) {
var reader = new FileReader();
reader.onload = function (event) {
var image = new Image();
image.onload = function() {
var testimage = new Image();
testimage.src = $(this).attr('src');
var widthOfImage = testimage.width;
var heightOfImage = testimage.height;
if (!checkImageDimensions(widthOfImage, heightOfImage)) {
// do stuff
} else {
returnThis = true;
}
callback(returnThis); // important
};
image.src = event.target.result;
holder.appendChild(image);
};
reader.readAsDataURL(file);
} else {
callback(returnThis); // important
}
}
function readfiles(files, tests, acceptedTypes, holder, progress) {
// important
previewfile(files[0], tests, acceptedTypes, holder, function (uploadNow) {
if (uploadNow === true) {
// do stuff
}
} else {
// do other stuff
}
}
});

Returning a variable from closure

How can I get the return value of that.whatever back from the closure? Instead of:
this.setCanvas = function(files){
var numItems = files.length - 1;
this.items = {};
var i = 0;
for(i=0;i<=numItems;i++)
{
var file = files[i];
var reader = new FileReader();
reader.onload = (function(i) {
return function(e) {
var something = that.whatever();
items[i] = something;
};
})(i);
reader.readAsDataURL(file);
}
console.log(items);
}
I need items[i] defined. If I console.log items[i] outside of the closure it is undefined.
The problem isn't the closure; it's the callback. Whatever needs to use the value of that.whatever needs to be executed in the callback.
You can augment your code to keep track of the number of files loaded. This way, when the last file has been loaded, you can invoke an ultimate completion handler:
this.setCanvas = function(files) {
var numItems = files.length - 1;
var itemsLoaded = 0; // Initialize to zero
var items = [];
var i = 0;
for(i=0;i<=numItems;i++) {
var file = files[i];
var reader = new FileReader();
reader.onload = (function(i) {
return function(e) {
var something = that.whatever();
items[i] = something;
if(++itemsLoaded == numItems) {
// At this point all files will have been loaded.
allLoaded();
}
};
})(i);
reader.readAsDataURL(file);
}
function allLoaded() {
// Now we can analyze the results
console.log(items);
}
}
I also changed items to be an Array instead of an Object.
Also if you want to be a little more clever, you could decrement numItems and check for zero instead of creating a new itemsLoaded variable.
There are a couple things you need to change here. First set this.items to an array. Next assign this to self so it can be referenced in the closure. Next assign the something to self.items[i] instead of items[i]. Finally use this.items in the console.log
this.setCanvas = function(files){
var self = this;
var numItems = files.length - 1;
this.items = [];
var i = 0;
for(i=0;i<=numItems;i++)
{
var file = files[i];
var reader = new FileReader();
reader.onload = (function(i) {
return function(e) {
var something = that.whatever();
self.items[i] = something;
};
})(i);
reader.readAsDataURL(file);
}
console.log(this.items);
}

Categories

Resources