I got a PDF embedded in my page, and I'd like to set something like a Javascript-callback to be called whenever the user clicks a link within the PDF.
Is there a way to accomplish this?
If you check out the Acrobat JS Reference for the minimum version you want to support, you'll see documentation on the HostContainer object.
In the PDF:
this.hostContainer.messageHandler =
{
onMessage: function(messageArray)
{
for(var i = 0; i < messageArray.length; i++)
console.println("Message " + i + ": " + messageArray[i]);
},
onError: function(error, messageArray){ },
onDisclose: function() {return true;}
};
In your HTML, assuming your PDF is inside an <object id="thePdf"> tag:
function messageFunc(messageArray) {
for(var i = 0; i < messageArray.length; i++)
alert("Message " + i + ": " + messageArray[i]);
}
document.getElementById("thePdf").messageHandler = { onMessage: messageFunc };
In your PDF, you'd also need to modify the links so they have a JS action that would post a message to the containing web page. This could be done programmatically (varying wildly depending on the language/library you use), or manually in Acrobat Pro.
this.hostContainer.postMessage(["urlClicked", "http://blah.blah.blah..."]);
Not terribly hard, but no one's ever heard of it. I'd be stunned if this worked anywhere outside an Adobe viewer (Reader/Acrobat) for the next several years.
If you want to send a message from your HTML to the PDF for some reason, it goes something like this:
var thePDF = document.getElementById("thePdf");
thePDF.postMessage(["This", "is", "an", "array", "too."]);
You could even yank out all the existing links and have the PDF request the links be opened by the wrapping HTML page... that way you can give the new windows names, close them from JS, etc. You can get down right snazzy.
But you have to be able to change the PDFs.
Your link is probably represented as a "Link annotation" inside your PDF file. Annotations in PDF can contain "Additional Actions", you could use a pdf processing software like iText (free for non commercial use) or Amyuni PDF Creator(commercial, usual disclaimer applis) to add a Javascript action to the "Additional Actions" collection of your link(s). You can invoke a page or method in your server using this Javascript code.
Client side:
PDF
$("#pdf-link").click(function(){
$.post('yoursite.com/phpfunctionpage/notifyPdf',
{event:'pdf'},
function(response){
alert(response['message']);
}, 'json'
);
});
Your server side:
function notifyPdf(){
$event = $_POST['event'];
if ($event == 'pdf'){
// handle notification here
echo json_encode(array('result'=>1, 'message'=>'notifiation successful'));
}
}
<a href="file.pdf" onclick:'javascriptFunction();'>Open the pdf file</a>
<script>
function javascriptFunction(){
}
</script>
Related
I am trying to return a file to the user.
"GetExcel" appears to work and in debug I can see that "ba" has data.
The method completes BUT nothing appears to be returned to the browser - I am hoping to see the file download dialog.
C#
public FileResult GetExcel()
{
using (ExcelPackage pck = new ExcelPackage())
{
ExcelWorksheet ws = pck.Workbook.Worksheets.Add("Demo");
ws.Cells["A1"].Value = "LBHERE";
var ba = pck.GetAsByteArray();
return File(ba, "text/plain", "testFile.txt");
}
}
Javascript
function clickedTest() {
alert("Test clicked");
$.get(myPath + "/Employee/GetExcel", { }, function (data) {
})
};
jQuery's $.get() function pulls data from a webpage into your client-side scripts through AJAX. This is not what you want to do if you want to download a file. Instead, you should open a new tab set to the URL of the file you wish to download.
Try this:
function clickedTest() {
window.open(myPath + "/Employee/GetExcel", "_blank");
}
If your browser still isn't initiating a download, but is instead just displaying a file, you may have to go one step further.
Somewhere in your server-side code, when you have access to the Response object, you should set the Content-Disposition header to attachment and provide a filename for the spreadsheet you are serving. This will inform your browser that the file you are requesting is meant to be downloaded, not displayed.
This can be done as follows:
Response.AddHeader("Content-Disposition", "attachment;filename=myfile.xls")
Of course, replace myfile.xls with the proper filename for your spreadsheet.
I have pdfs being generated on a website, and am trying to implement a print button for that pdf. I know about being able to print a specific div and it's associated html, but is it possible to have a button that opens up a pdf generated by my website in the chrome print preview without having to download it first?
I appreciate any help.
The pdf file that is generated on the server has to come down to the client (i.e. downloaded) so cannot really print something that is not on the client.
In case you want to bypass the file saving action (explicitly done by the user) and go directly to print preview, you can try something like this before returning the file:
var pdfFileName = "Some Filename";
if (Request.Browser.Browser == "InternetExplorer" || Request.Browser.Browser == "IE")
{
Response.AppendHeader("content-disposition", #"attachment;filename=""" + pdfFileName + #".pdf""");
}
else
{
Response.AppendHeader("content-disposition", #"inline;filename=""" + pdfFileName + #".pdf""");
}
byte[] pdfBytes = ... // get the file data into this variable
return File(pdfBytes, "application/pdf")
I don't think so. The PDF still has to be downloaded and rendered by the PDF library before it can be printed, unlike HTML which is the browser's native format.
Use Datatables.Net Export tables
I have a problem.
I have a list of SKU numbers (hundreds) that I'm trying to match with the title of the product that it belongs to. I have thought of a few ways to accomplish this, but I feel like I'm missing something... I'm hoping someone here has a quick and efficient idea to help me get this done.
The products come from Aidan Gray.
Attempt #1 (Batch Program Method) - FAIL:
After searching for a SKU in Aidan Gray, the website returns a URL that looks like below:
http://www.aidangrayhome.com/catalogsearch/result/?q=SKUNUMBER
... with "SKUNUMBER" obviously being a SKU.
The first result of the webpage is almost always the product.
To click the first result (through the address bar) the following can be entered (if Javascript is enabled through the address bar):
javascript:{document.getElementsByClassName("product-image")[0].click;}
I wanted to create a .bat file through Command Prompt and execute the following command:
firefox http://www.aidangrayhome.com/catalogsearch/result/?q=SKUNUMBER javascript:{document.getElementsByClassName("product-image")[0].click;}
... but Firefox doesn't seem to allow these two commands to execute in the same tab.
If that worked, I was going to go to http://tools.buzzstream.com/meta-tag-extractor, paste the resulting links to get the titles of the pages, and export the data to CSV format, and copy over the data I wanted.
Unfortunately, I am unable to open both the webpage and the Javascript in the same tab through a batch program.
Attempt #2 (I'm Feeling Lucky Method):
I was going to use Google's &btnI URL suffix to automatically redirect to the first result.
http://www.google.com/search?btnI&q=site:aidangrayhome.com+SKUNUMBER
After opening all the links in tabs, I was going to use a Firefox add-on called "Send Tab URLs" to copy the names of the tabs (which contain the product names) to the clipboard.
The problem is that most of the results were simply not lucky enough...
If anybody has an idea or tip to get this accomplished, I'd be very grateful.
I recommend using JScript for this. It's easy to include as hybrid code in a batch script, its structure and syntax is familiar to anyone comfortable with JavaScript, and you can use it to fetch web pages via XMLHTTPRequest (a.k.a. Ajax by the less-informed) and build a DOM object from the .responseText using an htmlfile COM object.
Anyway, challenge: accepted. Save this with a .bat extension. It'll look for a text file containing SKUs, one per line, and fetch and scrape the search page for each, writing info from the first anchor element with a .className of "product-image" to a CSV file.
#if (#CodeSection == #Batch) #then
#echo off
setlocal
set "skufile=sku.txt"
set "outfile=output.csv"
set "URL=http://www.aidangrayhome.com/catalogsearch/result/?q="
rem // invoke JScript portion
cscript /nologo /e:jscript "%~f0" "%skufile%" "%outfile%" "%URL%"
echo Done.
rem // end main runtime
goto :EOF
#end // end batch / begin JScript chimera
var fso = WSH.CreateObject('scripting.filesystemobject'),
skufile = fso.OpenTextFile(WSH.Arguments(0), 1),
skus = skufile.ReadAll().split(/\r?\n/),
outfile = fso.CreateTextFile(WSH.Arguments(1), true),
URL = WSH.Arguments(2);
skufile.Close();
String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ''); }
// returns a DOM root object
function fetch(url) {
var XHR = WSH.CreateObject("Microsoft.XMLHTTP"),
DOM = WSH.CreateObject('htmlfile');
WSH.StdErr.Write('fetching ' + url);
XHR.open("GET",url,true);
XHR.setRequestHeader('User-Agent','XMLHTTP/1.0');
XHR.send('');
while (XHR.readyState!=4) {WSH.Sleep(25)};
DOM.write(XHR.responseText);
return DOM;
}
function out(what) {
WSH.StdErr.Write(new Array(79).join(String.fromCharCode(8)));
WSH.Echo(what);
outfile.WriteLine(what);
}
WSH.Echo('Writing to ' + WSH.Arguments(1) + '...')
out('sku,product,URL');
for (var i=0; i<skus.length; i++) {
if (!skus[i]) continue;
var DOM = fetch(URL + skus[i]),
anchors = DOM.getElementsByTagName('a');
for (var j=0; j<anchors.length; j++) {
if (/\bproduct-image\b/i.test(anchors[j].className)) {
out(skus[i]+',"' + anchors[j].title.trim() + '","' + anchors[j].href + '"');
break;
}
}
}
outfile.Close();
Too bad the htmlfile COM object doesn't support getElementsByClassName. :/ But this seems to work well enough in my testing.
I followed the interactive live sdk and added it into my HTML page.
Also, I have successfully added the callback.html page where I am successfully getting the file picker dialog box. Once I select file from the file dialog box it's getting downloaded which I understand because of WL.download function.
But all I want is to attach the files rather than downloading it. how to change the javascript in interactive live sdk
Any suggestions?
Sorry about that. You can either use the "source" or the "link" to accomplish this. On the ISDK for "Using the open from OneDrive picker", change the code following code snippet (I used "file.link" below). The Output box should give you some idea on what the link would be if you include it in your app. You'll, of course, will want to remove the "WL.download" function so that it doesn't download the file and add the file.link or file.source somewhere into your code instead of logging it like the ISDK does.
function openFromSkyDrive() {
WL.fileDialog({
mode: 'open',
select: 'single'
}).then(
function(response) {
log("The following file is being downloaded:");
log("");
var files = response.data.files;
for (var i = 0; i < files.length; i++) {
var file = files[i];
log(file.link);
WL.download({ "path": file.id + "/content" });
}
},
function(errorResponse) {
log("WL.fileDialog errorResponse = " + JSON.stringify(errorResponse));
}
);
}
In the ISDK (http://isdk.dev.live.com), you'll want to try using the "Using the save to OneDrive picker". You'll notice that WL.fileDialog is set to ({ mode: 'save' }) and the WL.upload function is called. I hope that helps.
Summary
I am attempting to find out why the wl.download function will not download more than one file even though the Microsoft examples seem to indicate that they can.
And, the code seems to be called for each file you attempt to download, but only the one file is actually downloaded.
Details
Here are the details of how you can see this problem which I've tried in IE 11.x and Chrome 30.x
If you will kindly go to :
http://isdk.dev.live.com/dev/isdk/ISDK.aspx?category=scenarioGroup_skyDrive&index=0
You will be able to run an example app which allows you to download files from your skydrive.
Note: the app does require you to allow the app to access your skydrive.
Once you get there you'll see code that looks like this on the right side of the page:
Alter One Value: select:
You need to alter one value: Change the
select: 'single'
to
select: 'multi'
which will allow you to select numerous files to download to your computer. If you do not make that one change then you won't be able to choose more than one file in the File dialog.
Click the Run Button to Start
Next, you'll see a [Run] button to start the app (above the code sample).
Go ahead and click that button.
Pick Files For Download
After that just traverse through your skydrive files and choose more than one in a folder and click the [Open] button. At that point, you will see one of the files actually downloads, and a number of file names are displayed in the bottom (output) section of the example web page.
My Questions
Why is it that the others do not download, even though wl.download is called in the loop, just as the console.log is called in the loop?
Is this a known limitation of the browser?
Is this a known bug in skydrive API?
Is this just a bug in the example code?
The problem here is that the call to wl.download({ "path": file.id + "/content" }) stores some internal state (among other things, the file being downloaded and the current status thereof). By looping over the list of files, that state is in fact overwritten with each call. When I tried downloading three text files at once, it was always the last one that was actually downloaded and never the first two.
The difficulty here is that the downloads are executed in the traditional fashion, whereby the server adds Content-Disposition: attachment to the response headers to force the browser to download the file. Because of this, it is not possible to receive notification of any kind when the download has actually completed, meaning that you can't perform the downloads serially to get around the state problem.
One approach that I thought might work is inspired by this question. According to the documentation, we can get a download link to a file if we append /content?suppress_redirects=true to its id. Using this approach, we can set the src property of an IFrame and download the file that way. This works OK, but it will only force a download for file types that the browser can't natively display (zip files, Exe files, etc.) due to the lack of the Content-Disposition: attachment response header.
The following is what I used in the Interactive Live SDK.
WL.init({ client_id: clientId, redirect_uri: redirectUri });
WL.login({ "scope": "wl.skydrive wl.signin" }).then(
function(response) {
openFromSkyDrive();
},
function(response) {
log("Failed to authenticate.");
}
);
function openFromSkyDrive() {
WL.fileDialog({
mode: 'open',
select: 'multi'
}).then(
function(response) {
log("The following file is being downloaded:");
log("");
var files = response.data.files;
for (var i = 0; i < files.length; i++) {
var file = files[i];
log(file.name);
WL.api({
path: file.id + "/content?suppress_redirects=true",
method: "GET"
}).then(
function (response) {
var iframe = document.createElement("iframe");
iframe.src = response.location;
iframe.style.display = "none";
document.body.appendChild(iframe);
},
function (responseFailed) {
log("Error calling API: " + responseFailed.error.message);
}
);
}
},
function(errorResponse) {
log("WL.fileDialog errorResponse = " + JSON.stringify(errorResponse));
}
);
}
function log(message) {
var child = document.createTextNode(message);
var parent = document.getElementById('JsOutputDiv') || document.body;
parent.appendChild(child);
parent.appendChild(document.createElement("br"));
}
Did you try to bind some events to the WL.download() method? According to the documentation:
The WL.download method accepts only one parameter:
The required path parameter specifies the unique SkyDrive file ID of the file to download.
If the WL.download method call is unsuccessful, you can use its then method's onError parameter to report the error. In this case, the WL.download doesn't support the onSuccess and onProgress parameters. If the WL.download method call is successful, the user experience for actually downloading the files will differ based on the type of web browser in use.
Perhaps you are getting some errors in your log to identify the problem.
For me, one suggestion without having checked the documentation, I can think of the fact that you are not waiting for each download to end. Why not change your loop in such a manner that you call WL.download() only if you know no other download is currently running ( like calling the next WL.download only in the success/complete event ):
WL.download({ "path": file.id + "/content" }).then(
function (response) {
window.console && console.log("File downloaded.");
//call the next WL.download() here <!-----------------
},
function (responseFailed) {
window.console && console.log( "Error downloading file: " + responseFailed.error.message);
}
);