Removing a file in modern browsers - javascript

Problem
I am currently using ( https://github.com/blueimp/jQuery-File-Upload/wiki ) this jQuery HTML5 Uploader.
The basic version, no ui.
The big problem is, that I looked everywhere (Mozilla Developer Network, SO, Google, etc.) and found no solution for removing a files already added via dragNdrop or manually via the file input dialogue.
Why do I want to achieve removing a file?
Because it seems that HTML5 has a kind of "bug".
If you drop / select a file (file input has set multiple) upload it, and then drop / select another file you magically have now the new file twice and it gets uploaded twice.
To prevent this magic file caching the use would have to refresh the page, which is not what someone wants to have for his modern AJAX web app.
What I have tried so far:
.reset()
.remove()
Reset Button
Setting .val() to ''
This seems to be a general HTML5 JS problem not jQuery specific.
Theory
Might it be, that $j('#post').click (I bind / re-bind a lot of times different callbacks), stacks the callbacks methods so that each time the updateFileupload function is called an additional callback is set.
The actual problem would now not rely anymore on the HTML5 upload, it would now rely on my could, miss-binding the .click action on my submit button (id=#post).
If we now call .unbind before each .click there shouldn't be any duplicated callback binding.
Code
Function containing the upload code:
function updateFileupload (type) {
var destination = "";
switch(type)
{
case upload_type.file:
destination = '/wall/uploadfile/id/<?=$this->id?>';
break;
case upload_type.image:
destination = '/wall/upload/id/<?=$this->id?>';
break;
}
$j('#fileupload').fileupload({
dataType: 'json',
url: destination,
singleFileUploads: false,
autoUpload: false,
dropZone: $k(".dropZone"),
done: function (e, data) {
console.log("--:--");
console.log(data.result);
upload_result = data.result;
console.log(upload_result);
console.log("--:--");
console.log(type);
if(type == upload_type.image)
{
var imageName = upload_result.real;
console.log(imageName);
$k.get('/wall/addpicture/id/<?=$this->id ?>/name'+imageName, function(data){
if(data > 0){
console.log("I made it through!");
if(!data.id)
{
$k('#imgUpload').html('');
//$k('#imgPreview').fadeOut();
$k('#newPost').val('');
$k.get("/wall/entry/id/"+data, function(html){
$k('#postList').prepend(html);
});
}
}
});
}
},
send: function(e, data){
var files = data.files;
var duplicates = Array(); // Iterate over all entries and check whether any entry matches the current and add it to duplicates for deletion
for(var i=0; i<data.files.length;i++)
{
for(var j=0;j<data.files.length-1;j++)
{
if(files[i].name == files[j].name && i != j)
{
duplicates.push(j);
}
}
}
if(duplicates.length > 0)
{
for(var i=0;i<duplicates.length;i++)
files.splice(i, 1);
}
console.log("Duplicates");
console.log(duplicates);
},
drop: function(e, data){
console.log("outside");
// $k.each(data.files, function(index, file){
// $k('#imageListDummy').after('<li class="file-small-info-box">'+file.name+'</li>');
// console.log(file);
//
// });
},
add: function(e, data){
upload_data = data;
console.log(data);
$k.each(data.files, function(index, file){
$k('#imageListDummy').after('<li class="file-small-info-box">'+file.name+'</li>');
console.log(file);
});
$j('#post').click(function(event){
upload_data.submit();
if(type == upload_type.image)
{
var file = upload_data.files[0];
console.log("I am here");
console.log(file);
var img = document.createElement("img");
img.src = window.URL.createObjectURL(file);
img.height = 64;
img.width = 64;
img.onload = function(e) {
window.URL.revokeObjectURL(this.src);
}
document.getElementById('imgPreview').appendChild(img);
$k('#imgPreview').show();
}
clickPostCallback(event);
});
$j('#showSubmit').show();
}
});
}

It could be more a browser security issue.
Current file uploads specs don't allow javascript (or anything as far as I know) to tamper with the value of the file field even if to remove it.
So I would imagine any good file uploader would create multiple file upload fields so you can remove the entire field rather than play with the value?
This is speculation though.
Updated answer to Updated Question:
Shouldn't click() only be bound once? you shouldn't need to rebind a click event to a single element '#post' (unless this element changes, in which case it should really be a class). You can place the click() event binding outside of the options for file upload, as long as it's contained in a $(function(){} so it's when the DOM's ready.
Aside from that I'm trying to read the code without any HTML and no experience in multiple file uploading. The best thing to do is try and re-create it on jsfiddle.net, that way others can go in and play around with the code without affecting you and your likely to find the problem while putting the code in there anyway :)

Related

AJAX call not firing from inside if statement

I have the following code. There is a button in the UI that when clicked executes the if statement. I pass in a URL from a database and compare it to the current URL the user is on. If they match I want to run the code below, else I want to open the correct tab then run the code below.
With this code below I mean everything below starting from $('#sceanrioDropdownList').change(function () {...}. The code then checks a drop down and gets the selected Id from which an AJAX call is made to my web API that uses that Id in a stored procedure to return the results. The returned data is then iterated over and stored in variables which I am using to append to specific inputs, buttons and drop downs.
This is what I have so far and I think I have developed this correctly. The issue that I am currently having is that the UI wants everything from ... to be run if the if statement is true. I have tried CTRL+C and CTRL+V to copy the code into the if statement. I have also tried putting it in a new function and referencing that function n the if statement. Both do not work and I was using console.log to inspect the returned data.
It does however when I attempt to call it from inside i statement it doesn't return any data or error. It just doesn't seem to fire.
Is there a way in which I can achieve the functionality I desire? Do you have any suggestions as to if I have done something wrong. Thanks in advance.
$('#automate').click(automateButton);
function automateButton() {
if (webpageUrl == activeTabUrl) {
// do nothing
} else {
// Window opens
window.open(webpageUrl);
}
}
$('#scenarioDropdownList').change(function() {
var scenarioId = $('#scenarioDropdownList option:selected').prop('id');
getData(scenarioId);
});
function getData(scenarioId) {
$.ajax({
type: "GET",
url: 'http://localhost:54442/api/scenariodatas/GetScenarioData',
data: {
scenarioId: scenarioId
},
dataType: 'JSON',
success: scenarioData,
error: function() {
console.log("There has been an error retrieving the data");
}
});
}
function scenarioData(response) {
$.each(response, function(key, val) {
var fieldType = val.fieldType;
var fieldName = val.fieldName;
var fieldValue = val.fieldValue;
var field = $(fieldName);
if (field != undefined) {
switch (fieldType) {
case "Input":
$(field).val(fieldValue);
break;
case "Button":
$(field).click();
break;
case "Select":
$(field).val(fieldValue);
break;
}
}
})
}
onChange don´t work well with buttons because onChange detect a change in the value of your component, because of this, it´s highly recommended to use onClick when you use a button.
$('#scenarioDropdownList').click(function() {
var scenarioId = $('#scenarioDropdownList option:selected').prop('id');
getData(scenarioId);
});
I recommend you to put alerts when you are trying to test this sort of JS
EJM:
$('#scenarioDropdownList').change(function() {
alert('button active');
var scenarioId = $('#scenarioDropdownList option:selected').prop('id');
getData(scenarioId);
});
this alert allow you to know if the code is firing or not

JQuery $.post callback firing a function that never finishes

Here's the problem. I'm making a callback to the server that receives an MVC partial page. It's been working great, it calls the success function and all that. However, I'm calling a function after which iterates through specific elements:
$(".tool-fields.in div.collapse, .common-fields div.collapse").each(...)
Inside this, I'm checking for a specific attribute (custom one using data-) which is also working great; however; the iterator never finishes. No error messages are given, the program doesn't hold up. It just quits.
Here's the function with the iterator
function HideShow() {
$(".tool-fields.in div.collapse, .common-fields div.collapse").each(function () {
if (IsDataYesNoHide(this)) {
$(this).collapse("show");
}
else
$(this).collapse("hide");
});
alert("test");
}
Here's the function called in that, "IsDataYesNoHide":
function IsDataYesNoHide(element) {
var $element = $(element);
var datayesnohide = $element.attr("data-yes-no-hide");
if (datayesnohide !== undefined) {
var array = datayesnohide.split(";");
var returnAnswer = true;
for (var i in array) {
var answer = array[i].split("=")[1];
returnAnswer = returnAnswer && (answer.toLowerCase() === "true");
}
return returnAnswer;
}
else {
return false;
}
}
This is the way the attribute appears
data-yes-no-hide="pKanban_Val=true;pTwoBoxSystem_Val=true;"
EDIT: Per request, here is the jquery $.post
$.post(path + conPath + '/GrabDetails', $.param({ data: dataArr }, true), function (data) {
ToggleLoader(false); //Page load finished so the spinner should stop
if (data !== "") { //if we got anything back of if there wasn't a ghost record
$container.find(".container").first().append(data); //add the content
var $changes = $("#Changes"); //grab the changes
var $details = $("#details"); //grab the current
SplitPage($container, $details, $changes); //Just CSS changes
MoveApproveReject($changes); //Moves buttons to the left of the screen
MarkAsDifferent($changes, $details) //Adds the data- attribute and colors differences
}
else {
$(".Details .modal-content").removeClass("extra-wide"); //Normal page
$(".Details input[type=radio]").each(function () {
CheckOptionalFields(this);
});
}
HideShow(); //Hide or show fields by business logic
});
For a while, I thought the jquery collapse was breaking, but putting the simple alert('test') showed me what was happening. It just was never finishing.
Are there specific lengths of time a callback function can be called from a jquery postback? I'm loading everything in modal views which would indicate "oh maybe jquery is included twice", but I've already had that problem for other things and have made sure that it only ever includes once. As in the include is only once in the entire app and the layout is only applied to the main page.
I'm open to any possibilities.
Thanks!
~Brandon
Found the problem. I had a variable that was sometimes being set as undefined cause it to silently crash. I have no idea why there was no error message.

How would you cycle through each image and check it for an error in a casperjs script?

How would you cycle through every image on a given page and check to see if it was loaded or errored out?
The following are not seeming to work in the phantomjs/casperjs setup yet cycling through and grabbing the src is working.
.load()
and
.error()
If the above does work, could someone show a proper usage that would work in a casperjs script?
The code I used looked similar to the following:
var call_to_page = this.evaluate(function() {
var fail_amount = 0;
var pass_amount = 0;
var pass_img_src = [];
var fail_img_src = [];
var img_src = [];
$("img").each(function(){
$(this).load(function(){
pass_amount++;
pass_img_src.push($(this).attr("src"));
}).error(function(){
fail_amount++;
fail_img_src.push($(this).attr("src"));
});
img_src.push($(this).attr("src"));
});
return [img_src, fail_amount, fail_img_src, pass_amount, pass_img_src];
});
Any help as to why the above code doesnt work for me would be great. The page is properly being arrived upon and I am able to mess with the dom, just not .load or .error. Im thinking its due to the images being done loaded, so Im still looking for alternatives.
CasperJS provides the resourceExists function which can be used to check if a particular ressource was loaded. The ressource is passed into a function, so custom constraints can be raised.
casper.then(function(){
var fail_amount = 0;
var pass_amount = 0;
var pass_img_src = [];
var fail_img_src = [];
var elements = this.getElementsInfo("img");
elements.forEach(function(img){
if (img && img.attributes && casper.resourceExists(function(resource){
return resource.url.match(img.attributes.src) &&
resource.status >= 200 &&
resource.status < 400;
})) {
pass_amount++;
pass_img_src.push(img.attributes.src);
} else {
fail_amount++;
fail_img_src.push(img.attributes.src);
}
});
this.echo(JSON.stringify([fail_amount, fail_img_src, pass_amount, pass_img_src], undefined, 4));
});
This can be done after the page is loaded. So there is no need to preemptively add some code into the page context.
In turn, the problem with you code may be that the callbacks never fire because the images are already loaded of already timed out. So there is no new information.
If you're not sure what kind of errors are counted, you can use a custom resources detection for all available types or errors.
var resources = []; // a resource contains at least 'url', 'status'
casper.on("resource.received", function(resource){
if (resource.stage == "end") {
if (resource.status < 200 || resource.status >= 400) {
resource.errorCode = resource.status;
resource.errorString = resource.statusText;
}
resources.push(resource);
}
});
casper.on("resource.timeout", function(request){
request.status = -1;
resources.push(request);
});
casper.on("resource.error", function(resourceError){
resourceError.status = -2;
resources.push(resourceError);
});
function resourceExists(url){
return resources.filter(function(res){
return res.url.indexOf(url) !== -1;
}).length > 0;
}
casper.start(url, function(){
var elements = this.getElementsInfo("img");
elements.forEach(function(img){
if (img && img.attributes && resourceExists(img.attributes.src) && !resourceExists(img.attributes.src).errorCode) {
// pass
} else {
// fail
}
});
});
I don't have much experience with caperjs, in my observation I identified below points
Note:
jQuery .load( handler ) and .error( handler ) both were deprecated from verson 1.8.
If you're using jQuery 1.8+ then attaching load and error events to img(tags) does nothing.
jQuery Ajax module also has a method named $.load() is shortcut form of $.get(). Which one is fired depends on the set of arguments passed.
Here are Caveats of the load event when used with images from jQuery Docs
A common challenge developers attempt to solve using the .load() shortcut is to execute a function when an image (or collection of images) have completely loaded. There are several known caveats with this that should be noted. These are:
It doesn't work consistently nor reliably cross-browser
It doesn't fire correctly in WebKit if the image src is set to the same src as before
It doesn't correctly bubble up the DOM tree
Can cease to fire for images that already live in the browser's cache
So if you've version 1.8+ of jQuery the below block does nothing.
$(this).load(function(){
pass_amount++;
pass_img_src.push($(this).attr("src"));
}).error(function(){
fail_amount++;
fail_img_src.push($(this).attr("src"));
});
As a result this return [img_src, fail_amount, fail_img_src, pass_amount, pass_img_src]; statement will give us only img_src[with number of imgs as length] array filled with srcs of imgs in page. and other elements fail_amount, fail_img_src, pass_amount, pass_img_src will have same defaults all the time.
In the case of jQuery 1.8 below load and error events attachment with jQuery were meaningful(in your case these events were attached after they were loaded on page, so they won't show any effect with load and error callbacks), but the time where we attach events matters. We should attach these before the img tags or place the events in tag level(as attributes onload & onerror) and definitions of function handlers script should keep before any img tag or in very beginning of the body or in head
There're ways to figure out some are here:
use open-source plug-in like (Use imagesLoaded](https://github.com/desandro/imagesloaded)
Can use ajax call to find out whether the img.src.url were good or not?
Dimension based check like below function IsImageRendered
below I've but its old one not sure the browser support at this time. i recommend to go with above plug in if you can use it
var call_to_page = this.evaluate(function () {
function isImageRendered(img) {
// with 'naturalWidth' and 'naturalHeight' to get true size of the image.
// before we tried width>0 && height>0
if (typeof img.naturalWidth !== "undefined" && img.naturalWidth === 0) {
return false;
}
//good
return true;
}
var pass_img_src = [];
var fail_img_src = [];
var img_src = [];
$("img").each(function () {
var $img = $(this), $srcUrl = $img.attr("src");
img_src.push($srcUrl);
if (!$srcUrl)
fail_img_src.push($srcUrl);
else {
if (isImageRendered($img.get(0))) {
pass_img_src.push($srcUrl);
}
else {
fail_img_src.push($srcUrl);
}
}
});
var fail_count = fail_img_src.length,
pass_count = pass_img_src.length;
return [img_src, fail_count, fail_img_src, pass_count, pass_img_src];
});

How to abort an XHR connection when there are multiple running simultaneously?

I'm working on a file uploader, where about 10 files upload at the same time via a for loop.
Now I am trying to create a cancel button to cancel ALL the uploads, however with my current code, only the very last upload will cancel.
I've included my boiled down code, but basically its a loop which goes through an array of images (theAttach) and for each image it sets up an xhrAttach to send the images. So about say 10 images start uploading at the same time.
If a cancel button is pressed, I run the command xhrAttach.abort(); but only the very last image aborts.
Any ideas?
for (var i=0;i<theAttach.length;i++)
{
var xhrAttach = Ti.Network.createHTTPClient();
xhrAttach.timeout = 15000;
xhrAttach.onsendstream = function(e){
};
xhrAttach.onreadystatechange = function() {
if (xhrAttach.readyState != 4) return;
if ((i == theAttach.length) && (xhrAttach.readyState == 4))
{
}
};
xhrAttach.onerror = function() {
};
xhrAttach.open('POST', url, true);
xhrAttach.setRequestHeader('User-Agent', theuseragent());
xhrAttach.send(AttachmentTransmitArray);
}
Cocco nailed it! He suggested I cache each xhr into a container array, therefore I could access the individual xhr and abort it that way ie xhrAttach[i].abort()
I did this and it works perfectly! Thanks cocco!

Why isn't my callback being called on one page only?

I am using jQuery to grab some JSON and then plug it into some elements and display it on my page.
It works fine on all pages except one, where the response seems to be the page itself.
I have placed alert()s in the callbacks (success and complete) and they never seem to be fired (though Firebug shows the request returning 200 OK which should trigger the success handler).
I don't know what to do, I've never encountered this before.
Here is the jQuery code I am using:
var specials = (function() {
var specials = false,
specialsAnchor;
var init = function() {
specialsAnchor = $('#menu-specials a');
specialsAnchor.click(function(event) {
event.preventDefault();
if (specials != false && specials.is(':visible')) {
hide();
} else {
show();
}
});
};
var load = function(callback) {
specialsAnchor.addClass('loading');
specials = $('<div />', { 'id': 'specials' }).hide().appendTo('#header');
var specialsUl = $('<ul />').appendTo(specials);
$.ajax(specialsAnchor.attr('href'), {
dataType: 'json',
success: function(data) {
$.each(data, function(i, special) {
specialsUl.append('<li><h4>' + special.heading + '</h4><p>' + special.content + '</p></li>');
});
specialsAnchor.removeClass('loading');
callback.call();
}
});
}
var show = function() {
if (specials == false) {
load(show);
return;
}
specials.slideDown(500);
}
var hide = function() {
specials.slideUp(500);
}
$(init);
})();
What is going on?
I noticed that you're including jquery.validate on this page, but not the others. jQuery validate with jQuery > 1.5 causes some issues with AJAX calls.
I realize the linked question/answer aren't exactly what you're seeing, but I've seen all kinds of weird issues with AJAX calls and this combination of validate and jQuery, so I figured it would be worth mentioning.
Hope that helps.
This is probably not a complete answer, but could be a step in the right direction. Using Charles Proxy it seems on your other pages when I click specials, it makes a request to http://www.toberua.com/~new/specials however on the contact-us page the ajax request is instead going to http://www.toberua.com/~new/contact-us (which of course is not json)
One other interesting note:
The XMLHttpRequest on other pages sets the Accept header properly (i.e. Accept application/json, text/javascript, */*; q=0.01 , whereas on the contact-us page it is set to Accept */*). I'd bet there's a different code branch being invoked...

Categories