Following this SO solution here to notify clients of a click event in a PDF document, how is it possible to notify the client when the PDF gets submitted by the client using this.myPDF.submitForm("localhost/Handler.ashx?r=2) function?
The PDF File is created inside a user control then rendered into a HTML object:
string container = ("<object data='/myfile.pdf' type='application/pdf'></object>");
The JS file attached to the PDF is done like this:
var webClient = new WebClient();
string htmlContent = webClient.DownloadString(fileurl + "pdf_script.js");
PdfAction action = PdfAction.JavaScript(htmlContnent, pdfstamper.Writer);
pdfstamper.Writer.SetOpenAction(action);
And the content of the js file:
this.disclosed = true;
if (this.external && this.hostContainer) {
function onMessageFunc(stringArray) {
try {
this.myPDF.submitForm("http://localhost/Handler.ashx?EmpNo=12345" + "#FDF", false);
}
catch (e) {
}
}
function onErrorFunc(e) {
console.show();
console.println(e.toString());
}
try {
if (!this.hostContainer.messageHandler);
this.hostContainer.messageHandler = new Object();
this.hostContainer.messageHandler.myPDF = this;
this.hostContainer.messageHandler.onMessage = onMessageFunc;
this.hostContainer.messageHandler.onError = onErrorFunc;
this.hostContainer.messageHandler.onDisclose = function () { return true; };
}
catch (e) {
onErrorFunc(e);
}
}
When the submitForm call is made the PDF contents (form fields) get saved successfully and an alert is displayed in the PDF by doing this:
message = "%FDF-1.2
1 0 obj
<<
/FDF
<<
/Status("Success!")
>>
>>
endobj
trailer
<</Root 1 0 R>>
%%EOF");
return message;
What I'm trying to do is to get the PDF to callback the client after the form submit call sent from this client, a way to acknowledge the client that the form has been submitted, not in a form of an alert, but rather, a way to trigger a function in the host (the container, an iframe, object...etc).
The FDF response you used was unknown to me, so I've learned something new from your question. I've studied the AcroJS Reference and the FDF specification in the PDF Reference, and now I have a better understanding of what your code does. Thank you for that.
I assume that you already know how to trigger a JavaScript message in an HTML file using a JavaScript call from a PDF. See the createMessageHandler() in the JavaScript Communication between HTML and PDF article.
I interpret your question as: "How to I invoke this method after a successful submission of the data?"
If there's a solution to this question, it will involve JavaScript. I see that one can add JavaScript in an FDF file, but I'm not sure if that JavaScript can 'talk to' HTML. I'm not sure if you can call a JavaScript function in your initial PDF from the FDF response. If it's possible, you should add a JavaScript entry to your PDF similar to the /Status entry.
The value of this entry is a dictionary, something like:
<<
/Before (app.alert\("before!"\))
/After (app.alert\("after"\))
/Doc [/MyDocScript1, (myFunc1\(\)),
/MyDocScript2, (myFunc2\(\))
>>
In your case, I would remove the /Before and /Doc keys. I don't think you need them, I'd reduce the dictionary to:
<<
/After (talkToHtml\(\))
>>
Where talkToHtml() is a method already present in the PDF:
function talkToHtml() {
var names = new Array();
names[0] = "Success!";
try{
this.hostContainer.postMessage(names);
}
catch(e){
app.alert(e.message);
}
}
I don't know if this will work. I've never tried it myself. I'm basing my answer on the specs.
I don't know if you really need to use FDF. Have you tried adding JavaScript to your submitForm() method? Something like:
this.myPDF.submitForm({
cURL: "http://localhost/Handler.ashx?EmpNo=12345",
cSubmitAs: "FDF",
oJavaScript: {
Before: 'app.alert("before!")',
After: 'app.alert("after")',
Doc: ["MyDocScript1", "myFunc1()",
"MyDocScript2", "myFunc2()" ]
}
});
This will only work if you submit as FDF. I don't think there's a solution if you submit an HTML query string.
In case you're wondering what MyDocScript1 and MyDocScript2 are:
Doc defines an array defining additional JavaScript scripts to be
added to those defined in the JavaScript entry of the document’s name
dictionary. The array contains an even number of elements, organized
in pairs. The first element of each pair is a name and the second
is a text string or text stream defining the script corresponding
to that name. Each of the defined scripts is added to those already
defined in the name dictionary and then executed before the script
defined in the Before entry is executed. (ISO-32000-1 Table 245)
I'm not sure if all of this will work in practice. Please let me know either way.
Related
I have a javascript file that has code with DOM access
var a=document.getElementById("abc").value
I have html file, that contains all DOM information
<html>...<input id="abc" ...></html>
Is there anyway to get C# invoke the javascript file, and return the value of a, back to the c# program?
In reality the JavaScript can be much more complex, and I need to channel those "interested values" back to C#, but let's just consider the simple example mentioning here.
Possible directions I could think is using https://jint.codeplex.com/, or Web browser control. The challenge here is that it not only involves the JavaScript, it also involving the HTML file.
What I want to know is:
Is there a way to channel variable value from JavaScript back to C#?
How to get JavaScript evaluate DOM elements from a HTML file?
Using WebBrowser and Jint:
using (WebBrowser browser = new WebBrowser())
{
browser.ScriptErrorsSuppressed = true;
browser.DocumentText = #"<html><head/><body><input id=""abc"" value=""this is the value in input""></body></html>";
// Wait for control to load page
while (browser.ReadyState != WebBrowserReadyState.Complete)
Application.DoEvents();
dynamic d = (dynamic)browser.Document.DomDocument;//get de activex dom
var jengine = new Jint.Engine();
jengine.SetValue("document", d);
try
{
string val=jengine.Execute(#"var a=document.getElementById('abc').value;").GetValue("a").ToString();
Console.WriteLine(val);
}
catch (Jint.Runtime.JavaScriptException je)
{
Console.WriteLine(je);
}
}
I've been trying to implement a few file uploader packages (raw php) in a dynamically loaded format, with a potentially unknown number of them on a page. I've pretty much looked at all the popular ones from flash based to everything in between but having the same problem.
I'm currently trying to work with this.
I have tried tricks I've read about like using ^= in getelementbyid and also "\\S*" to ensure that the relevant div id is used by the javascript but I've had no success. I've also tried adding a class name to each div and using the getelementbyclass without success. I've searched all over for a solution but I'm just not getting it.
Either I'm doing it wrong or I'm completely lost ... I'm actually both!
If anyone can put me out of my misery or send me down the right direction I'd really appreciate as I've been trying to find a solution for a while.
The HTML portion is presented like so:
<p id="upload" class="hidden"><label>Drag & drop not supported, but you can still upload via this input field:<br><input type="file"></label></p>
<p id="filereader">File API & FileReader API not supported</p>
<p id="formdata">XHR2's FormData is not supported</p>
<p id="progress">XHR2's upload progress isn't supported</p>
<p>Upload progress: <progress id="uploadprogress" min="0" max="100" value="0">0</progress></p>
<p>Drag an image from your desktop on to the drop zone above to see the browser both render the preview, but also upload automatically to this server.</p>
I intend to dynamically generate it via PHP into something like, where $inc is a php variable in a loop, e.g id="filereader$inc
The issue, I believe is where the javascript will only handle pre-defined or one instance of the uploader untill modified otherwise
E.g: filereader: document.getElementById('filereader')
That Javascript code won't work with multiple instances because it uses the getElementById method which returns only one element (the first match it finds).
It's also worth mentioning that the JS code won't work for many users (about half, I believe) because it relies on the File API which isn't supported by IE9 and below.
To make that code work with multiple instances, it must be either refactored so as not to rely on element IDs, or you must generate multiple copies with unique elment IDs with the server side code. In PHP, that would involve using a for or foreach loop similar to this:
<?php
for (i=0; $i<$something; i++) {
echo <<<EOB
var holder = document.getElementById('holder$i'),
tests = {
filereader: typeof FileReader != 'undefined',
dnd: 'draggable' in document.createElement('span'),
formdata: !!window.FormData,
progress: "upload" in new XMLHttpRequest
},
support = {
filereader: document.getElementById('filereader$i'),
formdata: document.getElementById('formdata$i'),
progress: document.getElementById('progress$i')
},
// snipped for space
EOB;
}
I don't recommend either course of action, primarily because it still won't work at all with a large number of users. Instead, I suggest using a different upload library.
I've been similarly dissatisfied with JS file upload libraries, so I wrote one that supports IE7+ (theoretically IE6, but not tested), progress bars (with fallback options for handling IE9 and below), and allows multiple instances.
It does not, however, support drag and drop uploading, so if that's a requirement, it won't suit your needs. For what it's worth, here it is (link to live demo at the top):
https://github.com/LPology/Simple-Ajax-Uploader
Okay if i understand it correct, you want to use Drag And Drop fields in differnt parts of your page.
so the short answer is, you would have to add the events to every div, that should have this functionality.since the upload works automatically you just need one file input field
only the dropzones must be copied(but only if you want, more dropzone) and their evens must be adjusted
Ps.: the html part stays the same as in the blog (plus the div with id holder [and holder2 for the second dropzone], which seem to have been omitted)
example:
// tested on Chrome 26+
....
if (tests.dnd) {
holder.ondragover = function () { this.className = 'hover'; return false; };
holder.ondragend = function () { this.className = ''; return false; };
holder.ondrop = function (e) {
this.className = '';
e.preventDefault();
readfiles(e.dataTransfer.files);
}
//.... other drop spots example need the same events ....
holder2.ondragover = function () { this.className = 'hover'; return false; };
holder2.ondragend = function () { this.className = ''; return false; };
holder2.ondrop = function (e) {
this.className = '';
e.preventDefault();
readfiles(e.dataTransfer.files);
}
} else {
...
}
...
this works, but you could make it niceer using jQuery or so.
i hope it helps
Edit:
If ou want to send files to differnt server-side-scripts you would have to "modify" the function readfiles in te onDrop event
example(possible solution, not very clean):
// tested on Chrome 26+
....
holder.ondrop = function (e) {
this.className = '';
e.preventDefault();
readfiles(e.dataTransfer.files, "script_1.php");
}
...
function readfiles(files, posturl) {
...
// now post a new XHR request
if (tests.formdata) {
var xhr = new XMLHttpRequest();
xhr.open('POST', posturl);
...
}
...
Or you could post to the same file, but using a flag to determin on the server from where the file came.
P.s.: you probably know, but still, this Codesnipplets and the one form the blog, need cleanup for production use.
all the best
I'm trying to use NetUtil.asyncCopy to append data from one file to the end of another file from a Firefox extension. I have based this code upon a number of examples at https://developer.mozilla.org/en-US/docs/Code_snippets/File_I_O, particularly the 'Copy a stream to a file' example. Given what it says on that page, my code below:
Creates nsIFile objects for the file to copy from and file to append to and initialises these objects with the correct paths.
Creates an output stream to the output file.
Runs the NetUtil.asyncCopy function to copy between the file (which, I believe, behaves as an nsIInputStream) and the output stream.
I run this code as append_text_from_file("~/CopyFrom.txt", "~/AppendTo.txt");, but nothing gets copied across. The Appending Text and After ostream dumps appear on the console, but not the Done or Error dumps.
Does anyone have any idea what I'm doing wrong here? I'm fairly new to both Firefox extensions and javascript (although I am a fairly experienced programmer) - so I may be doing something really silly. If my entire approach is wrong then please let me know - I would have thought that this approach would allow me to append a file easily, and asynchronously, but it may not be possible for some reason that I don't know about.
function append_text_from_file(from_filename, to_filename) {
var from_file = Components.classes["#mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
from_file.initWithPath(from_filename);
var to_file = Components.classes["#mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
to_file.initWithPath(to_filename);
dump("Appending text\n");
var ostream = FileUtils.openFileOutputStream(to_file, FileUtils.MODE_WRONLY | FileUtils.MODE_APPEND)
dump("After ostream\n");
NetUtil.asyncCopy(from_file, ostream, function(aResult) {
dump("Done\n");
if (!Components.isSuccessCode(aResult)) {
// an error occurred!
dump(aResult);
dump("Error!\n")
}
});
}
asyncCopy() requires an input stream not a file.
you can do this:
var fstream = Cc["#mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
fstream.init(from_file, 0x01, 4, null);
NetUtil.asyncCopy(fstream, ostream, function(aResult)....
I want a robust way to upload a file. That means that I want to be able to handle interruptions, error and pauses.
So my question is: Is something like the following possible using javascript only on the client.
If so I would like pointers to libraries, tutorials, books or implementations.
If not I would like an explanation to why it's not possible.
Scenario:
Open a large file
Split it into parts
For each part I would like to
Create checksum and append to data
Post data to server (the server would check if data uploaded correctly)
Check a web page on server to see if upload is ok
If yes upload next part if no retry
Assume all posts to server is accompanied by relevant meta data (sessionid and whatnot).
No. You can, through a certain amount of hackery, begin a file upload with AJAX, in which case you'll be able to tell when it's finished uploading. That's it.
JavaScript does not have any direct access to files on the visitor's computer for security reasons. The most you'll be able to see from within your script is the filename.
Firefox 3.5 adds support for DOM progress event monitoring of XMLHttpRequest transfers which allow you to keep track of at least upload status as well as completion and cancellation of uploads.
It's also possible to simulate progress tracking with iframes in clients that don't support this newer XMLHTTPRequest additions.
For an example of script that does just this, take a look at NoSWFUpload. I've been using it succesfully for about few months now.
It's possible in Firefox 3 to open a local file as chosen by a file upload field and read it into a JavaScript variable using the field's files array. That would allow you to do your own chunking, hashing and sending by AJAX.
There is some talk of getting something like this standardised by W3, but for the immediate future no other browser supports this.
Yes. Please look at the following file -
function Upload() {
var self = this;
this.btnUpload;
this.frmUpload;
this.inputFile;
this.divUploadArea;
this.upload = function(event, target) {
event.stopPropagation();
if (!$('.upload-button').length) {
return false;
}
if (!$('.form').length) {
return false;
}
self.btnUpload = target;
self.frmUpload = $(self.btnUpload).parents('form:first');
self.inputFile = $(self.btnUpload).prev('.upload-input');
self.divUploadArea = $(self.btnUpload).next('.uploaded-area');
var target = $(self.frmUpload).attr('target');
var action = $(self.frmUpload).attr('action');
$(self.frmUpload).attr('target', 'upload_target'); //change the form's target to the iframe's id
$(self.frmUpload).attr('action', '/trnUpload/upload'); //change the form's action to the upload iframe function page
$(self.frmUpload).parent("div").prepend(self.iframe);
$('#upload_target').load(function(event){
if (!$("#upload_target").contents().find('.upload-success:first').length) {
$('#upload_target').remove();
return false;
} else if($("#upload_target").contents().find('.upload-success:first') == 'false') {
$('#upload_target').remove();
return false;
}
var fid = $("#upload_target").contents().find('.fid:first').html();
var filename = $("#upload_target").contents().find('.filename:first').html();
var filetype = $("#upload_target").contents().find('.filetype:first').html();
var filesize = $("#upload_target").contents().find('.filesize:first').html();
$(self.frmUpload).attr('target', target); //change the form's target to the iframe's id
$(self.frmUpload).attr('action', action); //change the form's
$('#upload_target').remove();
self.insertUploadLink(fid, filename, filetype, filesize);
});
};
this.iframe = '' +
'false' +
'';
this.insertUploadLink = function (fid, filename, filetype, filesize) {
$('#upload-value').attr('value', fid);
}
}
$(document).ready(event) {
var myupload = new Upload();
myupload.upload(event, event.target);
}
With also using PHP's APC to query the status of how much of the file has been uploaded, you can do a progress bar with a periodical updater (I would use jQuery, which the above class requires also). You can use PHP to output both the periodical results, and the results of the upload in the iframe that is temporarily created.
This is hackish. You will need to spend a lot of time to get it to work. You will need admin access to whatever server you want to run it on so you can install APC. You will also need to setup the HTML form to correspond to the js Upload class. A reference on how to do this can be found here http://www.ultramegatech.com/blog/2008/12/creating-upload-progress-bar-php/
I'm working on a project (BrowserIO - go to browserio dot googlecode dot com if you want to check out the code and work on it. Help welcome!) in which I'm using Firefox's nsIFileInputStream in tandem with nsIConverterInputStream, per their example (https://developer.mozilla.org/en/Code_snippets/File_I%2F%2FO#Simple), but only a portion of the full data is being loaded. The code is:
var file = Components.classes["#mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
file.initWithPath(path);
var data = "";
var fstream = Components.classes["#mozilla.org/network/file-input-stream;1"].createInstance(Components.interfaces.nsIFileInputStream);
var cstream = Components.classes["#mozilla.org/intl/converter-input-stream;1"].createInstance(Components.interfaces.nsIConverterInputStream);
fstream.init(file, -1, 0, 0);
cstream.init(fstream, "UTF-8", 0, 0); // you can use another encoding here if you wish
var str = {};
cstream.readString(-1, str); // read the whole file and put it in str.value
data = str.value;
cstream.close(); // this closes fstream
If you want to see this behavior, checkout the code from the BrowserIO project page, and use Firebug to set a breakpoint at the data = str.value; line in file_io.js. Then select a text file from the list, and click the "Open" button. In Firebug, in the watch panel set a watch for str.value. Look at the file... It should be truncated, unless it's really short.
For reference, the code above is the main body of the openFile() function in trunk/scripts/file_io.js.
Anybody have any clue what's happening with this?
See nsIConverterInputStream; basically, -1 doesn't mean "give me everything" but rather "give me the default amount", which the docs claim is 8192.
More generally, if you want to exhaust the contents of an input stream, you have to loop until it's empty. Nothing in any of the stream contracts guarantees that the amount of data returned by a call is the entirety of the contents of the stream; it could even return less than it has immediately available if it wanted.
I discovered how to do the file read without converting, to avoid issues from not knowing the file encoding type. The answer is to use nsIScriptableInputStream with nsIFileInputStream:
var sstream = Components.classes["#mozilla.org/scriptableinputstream;1"].createInstance(Components.interfaces.nsIScriptableInputStream);
fstream.init(file, 0x01, 0004, 0);
sstream.init(fstream);
data = sstream.read(sstream.available());