Create CSV from html using flash, javascript, classic asp - javascript

Let me start by stating that my end goal is to create an Export to Comma Separated File (.csv) or Export to Excel button on a page of our site. Our site currently has a search mechanism that returns the data in XML. That XML is loaded into a Microsoft.XMLDOM object.
Dim objXMLDom
Set objXMLDOM = Server.CreateObject("Microsoft.XMLDOM")
...
Function LoadXML(strXML)
objXMLDOM.loadXML(strXML)
end function
This XML data is parsed into a variable called myData and is displayed in an HTML Grid using different columns & rows. Like I said above, ideally I would like to add a button to the page that can export to csv or Excel. I've searched around and found that this is not an easy task when the code is in Classic ASP...so I found a semi-work around.
The workaround is to use Downloadify. It's a jquery/flash script that allows you to copy code into the HTML of a DOM element and then pull that data back out all on the client side. It works for the most part...the only problem I'm having is adding in a new line character. I think it gets removed going into HTML and back out again.
Here's the code for how I put data into the element
for (i = 0; i < <%=RowCount %>; i++) {
parent.document.getElementById('data').innerHTML += '\n';
for (j = 0; j < <%=columnCount%>; j++) {
parent.document.getElementById('data').innerHTML += myData[i][j];
parent.document.getElementById('data').innerHTML += ',';
}
};
Here's what I use to pull it back out:
function load() {
Downloadify.create('downloadify', {
filename: function() {
return document.getElementById('filename').value;
},
data: function() {
return document.getElementById('data').value;
},
onComplete: function() { alert('Your File Has Been Saved!'); },
onCancel: function() { alert('You have cancelled the saving of this file.'); },
onError: function() { alert('You must put something in the File Contents or there will be nothing to save!'); },
swf: 'media/downloadify.swf',
downloadImage: 'images/download.png',
width: 100,
height: 30,
transparent: true,
append: false
}
);
}
I really don't know very much Flash, but I've tried putting in different strings ('<br>', '
', even ";:;" ) and replacing them in the data : function part using Flash's replace method, but it seems to hang and never finish processing (i think this is because I needed to put the replace method in a while loop since the replace method only replaces one instance and I have many).
Any help will be appreciated. Thank you in advance.

why don't you have the ASP create a .csv file along with the html page? It seems like that would be a lot easier.

I had a problem like this awhile back when I was using Flash and ASP together.
I remember calling ASP from Flash and it hanging. I THINK the fix was to add a "response.end" to the end of the ASP page. That seemed to tell the server, "Done. Back to Flash now." Sorry I can't be more sure. It was a long time ago and I can't find the code.

Related

How can I prevent reloading of javascript in camel?

I am a starter for camel.
I used javascript for validation logic implement in camel xml.
Initially, it takes some time to load javascript when the first event(a file with some records) comes in. This situation is find.
In this case, only the first record is slow because of loading time of javascript and the rest of the records is normally performed.
The problem is that next event(a file) is coming in.
Camel tries to load javascript again. So, it takes loading time to process each file, so the overall performance has been degraded.
I want to modify some logic so that camel can only load it once.
How can I solve this problem?
<unmarshal id="_FileParsing">
<bindy
classType="com.openmzn.ktds.dao.volte.input.VoLTEBody"
locale="korea" type="Fixed"/>
</unmarshal>
<to id="_validateParsing" uri="language:javascript:classpath:spring/rules/volte/volte.js"/>
<multicast id="_FileDistributor" parallelProcessing="false">
<toD id="_ProcessNRat" uri="direct:NRAT"/>
<toD id="_ProcessDrop" uri="direct:DROP"/>
</multicast>
Javascript File
var bodyList = exchange.in.getBody(ArrayList.class);
if(!CollectionUtils.isEmpty(bodyList)) {
for (total_count = 0; total_count < bodyList.size(); total_count++) {
uBody = bodyList[total_count];
enriched = enrich(uBody);
result = validate(enriched);
resultList.add(result);
...
}
function enrich(uBody) {
...
}
function validate(enriched) {
...
}
You can turn on cacheScript=true, see the docs
https://github.com/apache/camel/blob/master/docs/components/modules/ROOT/pages/language-component.adoc

Detect when an iframe is loaded

I'm using an <iframe> (I know, I know, ...) in my app (single-page application with ExtJS 4.2) to do file downloads because they contain lots of data and can take a while to generate the Excel file (we're talking anything from 20 seconds to 20 minutes depending on the parameters).
The current state of things is : when the user clicks the download button, he is "redirected" by Javascript (window.location.href = xxx) to the page doing the export, but since it's done in PHP, and no headers are sent, the browser continuously loads the page, until the file is downloaded. But it's not very user-friendly, because nothing shows him whether it's still loading, done (except the file download), or failed (which causes the page to actually redirect, potentially making him lose the work he was doing).
So I created a small non-modal window docked in the bottom right corner that contains the iframe as well as a small message to reassure the user. What I need is to be able to detect when it's loaded and be able to differenciate 2 cases :
No data : OK => Close window
Text data : Error message => Display message to user + Close window
But I tried all 4 events (W3Schools doc) and none is ever fired. I could at least understand that if it's not HTML data returned, it may not be able to fire the event, but even if I force an error to return text data, it's not fired.
If anyone know of a solution for this, or an alternative system that may fit here, I'm all ears ! Thanks !
EDIT : Added iframe code. The idea is to get a better way to close it than a setTimeout.
var url = 'http://mywebsite.com/my_export_route';
var ifr = $('<iframe class="dl-frame" src="'+url+'" width="0" height="0" frameborder="0"></iframe>');
ifr.appendTo($('body'));
setTimeout(function() {
$('.dl-frame').remove();
}, 3000);
I wonder if it would require some significant changes in both frontend and backend code, but have you considered using AJAX? The workflow would be something like this: user sends AJAX request to start file generating and frontend constantly polls it's status from the server, when it's done - show a download link to the user. I believe that workflow would be more straightforward.
Well, you could also try this trick. In parent window create a callback function for the iframe's complete loading myOnLoadCallback, then call it from the iframe with parent.myOnLoadCallback(). But you would still have to use setTimeout to handle server errors/connection timeouts.
And one last thing - how did you tried to catch iframe's events? Maybe it something browser-related. Have you tried setting event callbacks in HTML attributes directly? Like
<iframe onload="done()" onerror="fail()"></iframe>
That's a bad practice, I know, but sometimes job need to be done fast, eh?
UPDATE
Well, I'm afraid you have to spend a long and painful day with a JS debugger. load event should work. I still have some suggestions, though:
1) Try to set event listener before setting element's src. Maybe onload event fires so fast that it slips between creating element and setting event's callback
2) At the same time try to check if your server code plays nicely with iframes. I have made a simple test which attempts to download a PDF from Dropbox, try to replace my URL with your backed route's.
<script src="https://code.jquery.com/jquery-1.11.1.min.js"></script>
<iframe id="book"></iframe>
<button id="go">Request downloads!</button>
<script>
var bookUrl = 'https://www.dropbox.com/s/j4o7tw09lwncqa6/thinkpython.pdf';
$('#book').on('load', function(){
console.log('WOOT!', arguments);
});
$('#go').on('click', function(){
$('#book').attr('src', bookUrl);
});
</script>
UPDATE 2
3) Also, look at the Network tab of your browser's debugger, what happens when you set src to the iframe, it should show request and server's response with headers.
I've tried with jQuery and it worked just fine as you can see in this post.
I made a working example here.
It's basically this:
<iframe src="http://www.example.com" id="myFrame"></iframe>
And the code:
function test() {
alert('iframe loaded');
}
$('#myFrame').load(test);
Tested on IE11.
I guess I'll give a more hacky alternative to the more proper ways of doing it that the others have posted. If you have control over the PHP download script, perhaps you can just simply output javascript when the download is complete. Or perhaps redirect to a html page that runs javascript. The javascript run, can then try to call something in the parent frame. What will work depends if your app runs in the same domain or not
Same domain
Same domain frame can just use frame javascript objects to reference each other. so it could be something like, in your single page application you can have something like
window.downloadHasFinished=function(str){ //Global pollution. More unique name?
//code to be run when download has finished
}
And for your download php script, you can have it output this html+javascript when it's done
<script>
if(parent && parent.downloadHasFinished)
parent.downloadHasFinished("if you want to pass a data. maybe export url?")
</script>
Demo jsfiddle (Must run in fullscreen as the frames have different domain)
Parent jsfiddle
Child jsfiddle
Different Domains
For different domains, We can use postMessage. So in your single page application it will be something like
$(window).on("message",function(e){
var e=e.originalEvent
if(e.origin=="http://downloadphp.anotherdomain.com"){ //for security
var message=e.data //data passed if any
//code to be run when download has finished
}
});
and in your php download script you can have it output this html+javascript
<script>
parent.postMessage("if you want to pass data",
"http://downloadphp.anotherdomain.com");
</script>
Parent Demo
Child jsfiddle
Conclusion
Honestly, if the other answers work, you should probably use those. I just thought this was an interesting alternative so I posted it up.
You can use the following script. It comes from a project of mine.
$("#reportContent").html("<iframe id='reportFrame' sandbox='allow-same-origin allow-scripts' width='100%' height='300' scrolling='yes' onload='onReportFrameLoad();'\></iframe>");
Maybe you should use
$($('.dl-frame')[0].contentWindow.document).ready(function () {...})
Try this (pattern)
$(function () {
var session = function (url, filename) {
// `url` : URL of resource
// `filename` : `filename` for resource (optional)
var iframe = $("<iframe>", {
"class": "dl-frame",
"width": "150px",
"height": "150px",
"target": "_top"
})
// `iframe` `load` `event`
.one("load", function (e) {
$(e.target)
.contents()
.find("html")
.html("<html><body><div>"
+ $(e.target)[0].nodeName
+ " loaded" + "</div><br /></body></html>");
alert($(e.target)[0].nodeName
+ " loaded" + "\nClick link to download file");
return false
});
var _session = $.when($(iframe).appendTo("body"));
_session.then(function (data) {
var link = $("<a>", {
"id": "file",
"target": "_top",
"tabindex": "1",
"href": url,
"download": url,
"html": "Click to start {filename} download"
});
$(data)
.contents()
.find("body")
.append($(link))
.addBack()
.find("#file")
.attr("download", function (_, o) {
return (filename || o)
})
.html(function (_, o) {
return o.replace(/{filename}/,
(filename || $(this).attr("download")))
})
});
_session.always(function (data) {
$(data)
.contents()
.find("a#file")
.focus()
// start 6 second `download` `session`,
// on `link` `click`
.one("click", function (e) {
var timer = 6;
var t = setInterval(function () {
$(data)
.contents()
.find("div")
// `session` notifications
.html("Download session started at "
+ new Date() + "\n" + --timer);
}, 1000);
setTimeout(function () {
clearInterval(t);
$(data).replaceWith("<span class=session-notification>"
+ "Download session complete at\n"
+ new Date()
+ "</span><br class=session-notification />"
+ "<a class=session-restart href=#>"
+ "Restart download session</a>");
if ($("body *").is(".session-restart")) {
// start new `session`,
// on `.session-restart` `click`
$(".session-restart")
.on("click", function () {
$(".session-restart, .session-notification")
.remove()
// restart `session` (optional),
// or, other `session` `complete` `callback`
&& session(url, filename ? filename : null)
})
};
}, 6000);
});
});
};
// usage
session("http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf", "ECMA_JS.pdf")
});
jsfiddle http://jsfiddle.net/guest271314/frc82/
In regards to your comment about to get a better way to close it instead of setTimeout. You could use jQuery fadeOut option or any of the transitions and in the 'complete' callback remove the element. Below is an example you can dump right into a fiddle and only need to reference jQuery.
I also wrapped inside listener for 'load' event to not do the fade until the iFrame has been loaded as question originally was asking.
// plugin your URL here
var url = 'http://jquery.com';
// create the iFrame, set attrs, and append to body
var ifr = $("<iframe>")
.attr({
"src": url,
"width": 300,
"height": 100,
"frameborder": 0
})
.addClass("dl-frame")
.appendTo($('body'))
;
// log to show its part of DOM
console.log($(".dl-frame").length + " items found");
// create listener for load
ifr.one('load', function() {
console.log('iframe is loaded');
// call $ fadeOut to fade the iframe
ifr.fadeOut(3000, function() {
// remove iframe when fadeout is complete
ifr.remove();
// log after, should no longer exist in DOM
console.log($(".dl-frame").length + " items found");
});
});
If you are doing a file download from a iframe the load event wont fire :) I was doing this a week ago. The only solution to this problem is to call a download proxy script with a tag and then return that tag trough a cookie then the file is loaded. min while yo need to have a setInterval on the page witch will watch for that specific cookie.
// Jst to clearyfy
var token = new Date().getTime(); // ticks
$('<iframe>',{src:"yourproxy?file=somefile.file&token="+token}).appendTo('body');
var timers = [];
timers[timers.length+1] = setInterval(function(){
var _index = timers.length+1;
var cookie = $.cooke(token);
if(typeof cookie != "undefined"){
// File has been downloaded
$.removeCookie(token);
clearInterval(_index);
}
},400);
in your proxy script add the cookie with the name set to the string sent bay the token url parameter.
If you control the script in server that generates excel or whatever you are sending to iframe why don't you put a UID flag and store it in session with value 0, so... when iframe is created and server script is called just set UID flag to 1 and when script is finished (the iframe will be loaded) just put it to 2.
Then you only need a timer and a periodic AJAX call to the server to check the UID flag... if it's set to 0 the process doesn't started, if it's 1 the file is creating, and finally if it's 2 the process has been ended.
What do you think? If you need more information about this approach just ask.
What you are saying could be done for images and other media formats using $(iframe).load(function() {...});
For PDF files or other rich media, you can use the following Library:
http://johnculviner.com/jquery-file-download-plugin-for-ajax-like-feature-rich-file-downloads/
Note: You will need JQuery UI
You can use this library. The code snippet for you purpose would be something like:
window.onload = function () {
rajax_obj = new Rajax('',
{
action : 'http://mywebsite.com/my_export_route',
onComplete : function(response) {
//This will only called if you have returned any response
// instead of file from your export script
// In your case 2
// Text data : Error message => Display message to user
}
});
}
Then you can call rajax_obj.post() on your download link click.
Download
NB: You should add some header to your PHP script so it force file download
header('Content-Disposition: attachment; filename="'.$file.'"');
header('Content-Transfer-Encoding: binary');
There is two solutions that i can think of. Either you have PHP post it's progress to a MySQL table where from frontend will be pulling information from using AJAX calls to check up on the progress of the generation. Using somekind of unique key that is being generated when accessing the page would be ideal for multiple people generating excel files at the same time.
Another solution would be to use nodejs & then in PHP post the progress of the excel file using cURL or a socket to a nodejs service. Then when receiving updates from PHP in nodejs you simply write the progress of the excel file for the right socket. This will cut off some browser support though. Unless you go through with it using external libraries to bring websocket support for pretty much all browsers & versions.
Hope this answer helped. I was having the same issue previous year. Ended up doing AJAX polling having PHP post progress on the fly.
Try this:
Note: You should be on the same domain.
var url = 'http://mywebsite.com/my_export_route',
iFrameElem = $('body')
.append('<iframe class="dl-frame" src="' + url + '" width="0" height="0" frameborder="0"></iframe>')
.find('.dl-frame').get(0),
iDoc = iFrameElem.contentDocument || iFrameElem.contentWindow.document;
$(iDoc).ready(function (event) {
console.log('iframe ready!');
// do stuff here
});

How to structure my code to return a callback?

So I've been stuck on this for quite a while. I asked a similar question here: How exactly does done() work and how can I loop executions inside done()?
but I guess my problem has changed a bit.
So the thing is, I'm loading a lot of streams and it's taking a while to process them all. So to make up for that, I want to at least load the streams that have already been processed onto my webpage, and continue processing stream of tweets at the same time.
loadTweets: function(username) {
$.ajax({
url: '/api/1.0/tweetsForUsername.php?username=' + username
}).done(function (data) {
var json = jQuery.parseJSON(data);
var jsonTweets = json['tweets'];
$.Mustache.load('/mustaches.php', function() {
for (var i = 0; i < jsonTweets.length; i++) {
var tweet = jsonTweets[i];
var optional_id = '_user_tweets';
$('#all-user-tweets').mustache('tweets_tweet', { tweet: tweet, optional_id: optional_id });
configureTweetSentiment(tweet);
configureTweetView(tweet);
}
});
});
}};
}
This is pretty much the structure to my code right now. I guess the problem is the for loop, because nothing will display until the for loop is done. So I have two questions.
How can I get the stream of tweets to display on my website as they're processed?
How can I make sure the Mustache.load() is only executed once while doing this?
The problem is that the UI manipulation and JS operations all run in the same thread. So to solve this problem you should just use a setTimeout function so that the JS operations are queued at the end of all UI operations. You can also pass a parameter for the timeinterval (around 4 ms) so that browsers with a slower JS engine can also perform smoothly.
...
var i = 0;
var timer = setInterval(function() {
var tweet = jsonTweets[i++];
var optional_id = '_user_tweets';
$('#all-user-tweets').mustache('tweets_tweet', {
tweet: tweet,
optional_id: optional_id
});
configureTweetSentiment(tweet);
configureTweetView(tweet);
if(i === jsonTweets.length){
clearInterval(timer);
}
}, 4); //Interval between loading tweets
...
NOTE
The solution is based on the following assumptions -
You are manipulating the dom with the configureTweetSentiment and the configureTweetView methods.
Ideally the solution provided above would not be the best solution. Instead you should create all html elements first in javascript only and at the end append the final html string to a div. You would see a drastic change in performance (Seriously!)
You don't want to use web workers because they are not supported in old browsers. If that's not the case and you are not manipulating the dom with the configure methods then web workers are the way to go for data intensive operations.

How do I limit Kendo UI Web Upload To allow only a single upload?

I am currently using Kendo UI for uploading files to a DB Using MVC3 and Razor and Entity Framework. I have it working great in several areas of my site, except when I need to restrict it to allowing only a singular upload. I have multiple set to false, which I need to disallow multiple selections, but the user is still allowed to click the select button any number of times to add files, violating the requirements for this field in the DB.
I tried some suggestions I thought I found on their site, but they are referring to the current selected items sent in the current request, not the whole of the uploads list (see image below).
<script type="text/javascript">
function singleFile(e) {
var files = e.files;
if (e.files.length > 1) {
alert('Only one file may be uploaded, cancelling operation...');
e.preventDefault();
}
}
</script>
#(Html.Kendo().Upload()
.Name("resumeAttachments")
.Multiple(false)
.Async(async => async
.Save("ResumeSave", "File")
)
.Events(c => c
.Upload("resumeOnUpload")
)
.Events(c => c
.Success("resumeOnSuccess")
)
.Events(c => c
.Complete("singleFile")
)
)
After I was given the requirement to prevent multiple uploads I stumbled across this page.
"multiple" set to FALSE works just fine if it is done correctly.
(While you CAN use the Kendo Razor syntax, notice when you view the page source that the .Kendo() actually gets converted to .kendoUpload
Thus I prefer this syntax in javascript (after the #using):
#using Kendo.Mvc.UI;
<script type="text/javascript">
$(document).ready(function() {
$("#files").kendoUpload({"multiple":false,
async: {
saveUrl: '#Url.Action("Save", "Upload", new { typeOfUploadedFile= #Model.DocName.ToString(), #proposalNo = #Model.ProposalNo.ToString(), area = ""})',
removeUrl: '#Url.Action("Remove", "Upload")',
autoUpload: true
}
});
});
</script>
After a little bit of thinking over the weekend (and a long weekend of vacation to relax), it hit me... Changing the singleFile function to the following will disable the control after the file is uploaded.
function singleFile(e) {
var upload = $("#resumeAttachments").data("kendoUpload");
// disables the upload after upload
upload.disable();
}
You must multiple property of kendo upload set value to false;
for example #(Html.Kendo().Upload().Multiple(false))
I know this is a really old thread, however i just want to post the issues we have encounted as well.
Adding Multiple with async upload would work with below simple code
$("#files").kendoUpload({
multiple : false,
async : {
saveUrl : FileUploadURL,
removeUrl : FileRemoveURL,
autoUpload : true
},
remove : onRemove,
success : onSuccess
});
however there will be a really annoying behaviour that, when users select another file when the previous selected file uploading is still in progress, then from the front page it looks like the previous selected file is removed, but the fact is the file uploading of previous selected file will still continue, which means the file will get uploaded to your server anyway and you have no chance to trigger the removeUrl to delete the unused file, and of course consuming addtional bandwidth.
what we have done so far to get around this issue is to add a small handling in the onRemove event handler, which will invoking the clearFileByUid to stop the uploading.
function onRemove(e) {
for(var removedFileId of getFileId(e)){
//All in progress file should be stopped!
var fileEntry=$('.k-file-progress[' + kendo.attr('uid') + '="' + removedFileId + '"]', this.wrapper)
if(fileEntry!=null&&fileEntry.length>0){this.clearFileByUid(removedFileId);}
}
}
function getFileId(e) {
return $.map(e.files, function(file) {
var fileId = file.uid;
return fileId;
});
}
Also, adding:
var dropzone = $(".k-dropzone").addClass("hide");
to the singleFile() function will hide the select button after the upload is completed, giving the very real impression that you can no longer upload, given that:
.hide {
display: none; }
is defined in your css somewhere.
Restrict by setting multiple false in your script document-ready method.
$("#kendoSingleFileInput").kendoUpload({
multiple: false,
select: onSelect
});

Show a txt file on a webpage which updates every second

I'm sort of shooting in the dark here; I have no knowledge how to do this so some pointers and/or links to helpful tutorials would be great:
I have a website that I want to display a text file (server log). Probably embedded. The problem is, this file is updated whenever events happen in the server (faster than half a second usually). How can I make it so the webpage displays the file in real time, meaning showing a live feed of the file?
My guess is that it would use javascript and AJAX but my knowledge on both are pretty limited.
Any pointers and help would be appreciated :)
My answer uses PHP and Ajax though changing to ASP or any other language wont be hard.
In the head
<script type="text/javascript">
function Ajax()
{
var
$http,
$self = arguments.callee;
if (window.XMLHttpRequest) {
$http = new XMLHttpRequest();
} else if (window.ActiveXObject) {
try {
$http = new ActiveXObject('Msxml2.XMLHTTP');
} catch(e) {
$http = new ActiveXObject('Microsoft.XMLHTTP');
}
}
if ($http) {
$http.onreadystatechange = function()
{
if (/4|^complete$/.test($http.readyState)) {
document.getElementById('ReloadThis').innerHTML = $http.responseText;
setTimeout(function(){$self();}, 1000);
}
};
$http.open('GET', 'loadtxt.php' + '?' + new Date().getTime(), true);
$http.send(null);
}
}
</script>
In the Body
<script type="text/javascript">
setTimeout(function() {Ajax();}, 1000);
</script>
<div id="ReloadThis">Default text</div>
</body>
Now using loadtxt.php read the values of the text file
<?php
$file = "error.txt";
$f = fopen($file, "r");
while ( $line = fgets($f, 1000) ) {
print $line;
}
?>
Using jQuery, you could do the following
setInterval(function() {
$('#element').load('/url/to/file');
}, 1000);
Would refresh the div with ID element with the file contents every 1 second
You could use jQuery .get to get the file every few seconds and update the page to show the contents.
Others have talked about loading the log file every refresh but depending on the size of the file this migth be a bad idea. You might want to create a server side page that will read the log file and keep track of how much of it has already been given to you and only give you the new bits. If its a 10k file it would be annoying (and potentially laggy) to have this transferred to you every second.
Otherwise other people seem to have covered most of the client side stuff.
There are various ways of doing this...
You could look into long polling.
Stick a meta refresh tag to refresh the page every X seconds.
tail -f /path/to/log.log in terminal will open a live preview of the last few lines of that file - this is what I do if I need to read the error logs as I debug.
Or simply refresh the page manually as you go, it might be annoying having the page change it's contents automatically.
As you have said your file is very large, I would use the PHP file() function to just grab the first X amount of lines from a file to keep bandwith down and readability up!
use this
setInterval(function() {
jQuery.get('file.txt', function(data) {
alert(data);
//process text file line by line
$('#div').html(data.replace('n','
'));
});
}, 1000);
https://www.sitepoint.com/jquery-read-text-file/
Finally, yes the script and the div id="ReloadThis" work fine together ! It also works to display info from a PHP file which queries the text file so the incoming text can be formatted before being displayed in the div element.

Categories

Resources