Send a PDF URL in a browser to the printer via iframe - javascript

For current non-IE browsers (Chrome, Firefox, Opera, Safari), I would like to send a PDF document to the printer given a URL to that PDF.
To avoid superfluous windows popping up, I am presently doing this with an <iframe> but I would like to close the iframe after the printing is completed, otherwise some browsers will pop up a dialog when one tries to leave the page.
Here is what I have come up with so far (using lodash and jQuery for simplicity):
var _print_url, _remove_iframe, _set_print;
_print_url = function(src_url) {
$("<iframe src='" + src_url + "' type='application/pdf'>").css({
visibility: 'hidden',
position: 'fixed',
right: '0',
bottom: '0'
}).on('load', _set_print).appendTo(document.body);
};
_remove_iframe = function(iframe) {
return $(iframe).parent().find(iframe).detach();
};
_set_print = function() {
this.contentWindow.print();
/*
* Where we could do #contentWindow.close() with a window, we must remove the
* print iframe from the DOM. We have to engage in some async madness
* it seems, for no apparent reason other than this won't work
* synchronously (#cw.print above must be async, it seems) - even though
* window.close() appears to work synchronously.
*/
_.delay(_.partial(_remove_iframe, this), 100);
};
Sometimes it seems with Google Chrome the print-dialog ends up showing the PDF correctly, but when one selects the printer and confirms the intention to print it will actually send the contents of the frame's parent page to the printer instead of the PDF itself.
There is a link suggestions on the Mozilla page but this document seems obsolete at the moment. The best example I could find was by reverse-engineering the Amazon Web Services print dialog for invoices, but that opens a window.
One alternative I have considered is Google Cloud Print, but obviously this requires the installation of extra software or configuration of a Google Account, neither of which I would wish to impose on users unless necessary.
Are there other examples of how one might print a PDF given a URL, particularly with Javascript and without otherwise superfluous additional browser add-ons or artefacts such as windows popping up?

-- NOTE whit this approach you will never see the popup blocker --
I run in a similar problem wiht an ajax application a couple month ago, the problem was I faced was I need to create the pdf file and store before to send it to print, what I did is the following:
I didnt use iframes. This application works with php TCPDF for creating the pdf and jquery and underscore for the templating system.
you can see the demo video at http://www.screenr.com/Ur07 (2:18 min)
Send the information via JSON (ajax) To achieve this because I faced the problem that everything was in ajax, so I couldn't make post with the information, so what I did is to append a hidden form to the DOM and then with the target="_blank" make the post to a new window (which you will close a the end of the process).
HTML hidden virtual form
<form method='<%= method %>'
action="<%= action %>"
name="<%= name %>"
id="<%= id %>"
target="_blank">
<input type='hidden' name='json' id='<%= valueId %>' />
</form>
Javascript
function makePost(){
var _t = _.template(_JS_Templates["print.form.hidden"]); //this the html of the form
var o = {
method : "POST",
action : js_WEB_ + "/print.php",
name : "print_virtual_form",
id : "print_virtual_form_id",
valueId : "print_virtual_value"
}
var form = _t(o);
$(".warp").append(form); //appending the form to the dom
var json = {
data : data // some data
}
$("#print_virtual_value").val(JSON.stringify(json)); //assing to the hidden input the value and stringify the json
$("#print_virtual_form_id").submit(); //make the post submmitting the form
$("#print_virtual_form_id").remove(); //removing the "virtual hidden form"
}
2.- when you receive the json in my case was using php you create whatever you have to create, I did it in this way you can see the php code in this example (is my answer)
Generate PDF using TCPDF on ajax call
3.- And finally in this is where the cool parts come is the response, in this part, I use the php file to write a javascript code saying that have to close the parent windows and report the json answer to an specific function, is kind of callback.
switch($_SERVER['REQUEST_METHOD']){
case "GET":
echo "Denied";
break;
case "POST":
if(isset($_POST["json"])){
if(!empty($_POST["json"])){
$json = $_POST["json"];
$hash = PDF::makePDF($json);
echo "<script>
function init() {
//calling callback
//returning control to the previous browser
window.opener.someclass.finish('".$hash."');
window.close();
}
window.onload = init;
</script>";
}else{
echo "not json detected";
}
}else{
echo "not json detected";
}
break;
4.- and for the end with the control in you window browser you can do.
var someobject = (function(name){
//private
var finish(data){
//do whatever you want with the json data
}
//public
var $r = {};
$r.finish = function(data){
finish(data); //you will pass the data to the finish function and that is..!! voila!!
}
return $r;
})("someobject");
I know is not exactly what you ask but is another approach to the same problem while I think this is little more complex, I can guarantee works in a lot of browser and the users will love the way you do it. They will never see what is happening and they will download the file just how the expect to do it, clicking a link and the saving the file.
Just my two cents and happy coding.

Related

Chrome Extensions, Passing Large Variables to Other Windows via php

I'm currently writing a Chrome Extension that takes user selected data and passes it to another window. Aside from the manifest.json files, I have a background.js and index.php file.
The background.js file successfully grabs the user selected data, opens a new broswer window, and then passes the user selected data to the index.php file located on a server via $_GET. This works great until you exceed a certain character limit in which case you get an error saying the url is too long. I was wondering if there was anyway to send the data from the background.js file to the new window using $_POST or any other method that would allow larger data selections? Or could I just pass the user selected data to the new window first, and then have that window access the server?
background.js
function getClickHandler() {
return function(info, tab) {
var tx = info.selectionText;
tx = encodeURIComponent(tx);
// Create a new window to the info page.
var url = 'http://192.168.0.22?tx=' + tx;
chrome.windows.create({url: url, width: 500, height: 760 });
};
};
chrome.contextMenus.create({
"title" : "Store",
"type" : "normal",
"contexts" : ["selection"],
"onclick" : getClickHandler()
});
index.php
<?php
$text = $_GET['tx'];
print ($text);
?>
Potentially you could create a form dynamically and then set an input to the value you're trying to send as a query-string-parameter.
Something like:
var $form = $('<form action="' + url + '" method="POST" target="_blank" />'),
$input = $('<input name="tx" type="text" />').val(tx);
$form.append($input).trigger("submit");
Without running the code I'm not sure if the submit will function without adding the form to the DOM, if not then you'll need to append the form to the DOM and then trigger submit, after which you probably want to remove the form from the DOM.
I say potentially because I'm not familiar with writing Chrome Extensions but I've done this as part of a normal web-app before and it worked.

JavaScript: How to open a returned file via AJAX

This is similar to: How to open a file using JavaScript?
Goal: to retrieve/open a file on an image's double click
function getFile(filename){
// setting mime this way is for example only
var mime = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
jQuery.ajax({ url : 'get_file.pl',
data : {filename:filename},
success : function(data){
var win = window.open('','title');
win.document.open(mime);
win.document.write(data);
win.document.close();
}
});
}
jQuery('#imgID').dblclick(function(){
getFile('someFile.docx');
});
I'm doing this off the top of my head, but I think the above would work for text files, but not binary. Is there a plugin that does this properly? The ideal would be to open the file in the browser (or application), rather than download, but I doubt that is a dream. If the file must be downloaded with the save/open dialog, that's fine.
Edit:
One piece of information that I forgot to mention is that I'd like this to be a POST request. This is partly why I was looking at AJAX to begin with. I've seen workarounds that have created forms/iframes to do something similar, but I was looking for a better handler of the returned info.
Seems to me there's no reason to do this via AJAX. Just open the new window to get_file.pl?filename=... and let the browser handle it. If the user has a plugin capable of handling the Content-Type sent by get_file.pl, the file will display; otherwise, it should download like any other file.
function getFile(filename) {
window.open('get_file.pl?filename=' + filename,'title');
}
jQuery('#imgID').dblclick(function() {
getFile('someFile.docx');
});
Edit: If you want to POST to your script, you can do it with some <form> hackery:
function getFile(filename) {
var win = 'w' + Math.floor(Math.random() * 10000000000000);
window.open('', win,'width=250,height=100');
var f = $('<form></form>')
.attr({target: win, method:'post', action: 'get_file.pl'})
.appendTo(document.body);
var i = $('<input>')
.attr({type:'hidden',name:'filename',value:filename})
.appendTo(f);
f[0].submit();
f.remove();
}
Of course, this is somewhat silly since it is impossible to hide your data from "prying eyes" with developer tools. If your filename really is sensitive, issue access tokens to the client, and look up the data in your sever script.

IE8's information bar blocking a scripted file download in response to JQuery AJAX request

I have an html/javascript frontend that is using JQuery's AJAX request to send XML containing user-entered form data to a backend application which in turn creates a PDF from that information. The frontend receives a UUID in response, which it then uses in the download url to download the generated PDF.
This works wonderfully in Firefox and Safari, but is being blocked by Internet Explorer 8's protection against scripted downloads. Telling IE8 to download the file via the spawned Information Bar forces a reload of the page, which blanks out all of the entered user content.
A single onMouseUp event on a button-esque element is triggering the generation of the XML to send, sending the XML, getting its response, then initiating the download via setting the url in the window.location object. Separating out that download into a different button (having one generate and send the xml and fetch the UUID, and the other only initiate the download using the url made from the UUID) bypasses the information bar but ruins the simplicity and intuitiveness of the interface.
Here are the relevant javascript functions:
function sendXml()
{
var documentXml = generateDocumentXml();
var percentEncodedDocumentXml = escape(DocumentXml);
var url = "generate?document=" + percentEncodedDocumentXml;
$.ajax({
url: url,
type: "GET",
dataType: "xml",
success: function (xml)
{
var uuid = $(xml).find('uuid').text();
getPdf(uuid);
},
error: function (xhr)
{
alert("There was an error creating your PDF template");
}
});
}
function getPdf(uuid)
{
var url = "generate?get-pdf=" + uuid;
window.location = url;
}
I'm fishing for suggestions about how to best handle this issue. My first preference would be to have the information bar not interfere at all, but minimizing its harm would be a dramatic improvement over the current situation. If it could not reload and wipe the frontend interface, and actually proceed to downloading the file when the user chooses to "Download File..." via the Information Bar's menu, that would help.
I tested it and the reason for the bar to occur seems to be the fact, that there is no direct relation between the user-action(mouseover) and the loading of the URL(guess a PDF-file).
This workaround will solve the issue:
Create an iframe(may be hidden) inside the document and use
window.open(url,'nameAttributeOfTheIframe')
...to load the PDF. The bar occurs too, but if the user chooses to download, the current document will reload too, but the user-content(if you mean form-data) will remain, as the bar belongs to the iframe not to the parent document.
Be sure to send a attachment-header with the PDF too, to beware of showing it inside the browser(if the browser is able to), because if you use a hidden iframe the user cannot see what's loaded there.
<iframe name="nameAttributeOfTheIframe" style="display:none"></iframe>
<input type="button" value="click here" onclick="f1()"/>
<input value="default value">
<script type="text/javascript">
<!--
function f1()
{
//simulate delayed download
setTimeout(f2,1000)
}
function f2()
{
window.open('http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf','nameAttributeOfTheIframe');
}
document.getElementsByTagName('input')[1].value='this is modified value, should remain';
//-->
</script>

Writing to a new window with javascript... get access denied

I have been struggling with this for a while now, and decided it was time to ask for help.
I am trying to create a print function on an aspx page, and I am using javascript to do this:
function PrintContentCell() {
var display_setting = "toolbar=yes, location=no, directories=yes, menubar=yes,";
display_setting += "scrollbars=yes, width=750, height=600, left=100, top=25";
var content_innerhtml = document.getElementById("testTable").innerHTML;
var document_print = window.open("Loading.htm", "", display_setting);
document_print.document.open();
document_print.document.write('<html><head><title>Print</title></head>');
document_print.document.write('<body style="font-family:verdana; font-size:12px;" onLoad="self.print();self.close();" >');
document_print.document.write(content_innerhtml);
document_print.document.write('</body></html>');
document_print.print();
document_print.document.close();
return false;
}
I get "Access Denied" when the script tries to write to the new window. The Loading.htm file is just a very slim html document writing out the text "Loading...". I had hoped this would work after reading this thread: IE 6/7 Access Denied trying to access a popup window.document
Anybody can help?
If you want a new, empty popup window to write into, the usual approach is use an empty string for the URL:
window.open('', '_blank', features)
There's no point trying to open HTML from the server when you're immediately going to replace all the content before it's even had a chance to load. (Your empty window name "" may also cause problems.)
However, this is in any case not a good way to implement a “print version” of a page. Instead, use a print stylesheet which hides all but the contents of testTable.
Could you not simply .write() the "Loading" markup after creating an empty (window.open("", ...)) popup?
It would avoid a trip to the server, seem more responsive to the user and solve your problem.
Edit In-fact, as your just shuffling data about on the client side, does the time it takes to render the HTML really warrant a loading banner?

How to use javascript to get information from the content of another page (same domain)?

Let's say I have a web page (/index.html) that contains the following
<li>
<div>item1</div>
details
</li>
and I would like to have some javascript on /index.html to load that
/details/item1.html page and extract some information from that page.
The page /details/item1.html might contain things like
<div id="some_id">
picture
map
</div>
My task is to write a greasemonkey script, so changing anything serverside is not an option.
To summarize, javascript is running on /index.html and I would
like to have the javascript code to add some information on /index.html
extracted from both /index.html and /details/item1.html.
My question is how to fetch information from /details/item1.html.
I currently have written code to extract the link (e.g. /details/item1.html)
and pass this on to a method that should extract the wanted information (at first
just .innerHTML from the some_id div is ok, I can process futher later).
The following is my current attempt, but it does not work. Any suggestions?
function get_information(link)
{
var obj = document.createElement('object');
obj.data = link;
document.getElementsByTagName('body')[0].appendChild(obj)
var some_id = document.getElementById('some_id');
if (! some_id) {
alert("some_id == NULL");
return "";
}
return some_id.innerHTML;
}
First:
function get_information(link, callback) {
var xhr = new XMLHttpRequest();
xhr.open("GET", link, true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
callback(xhr.responseText);
}
};
xhr.send(null);
}
then
get_information("/details/item1.html", function(text) {
var div = document.createElement("div");
div.innerHTML = text;
// Do something with the div here, like inserting it into the page
});
I have not tested any of this - off the top of my head. YMMV
As only one page exists in the client (browser) at a time and all other (virtual/possible) pages are on the server, how will you get information from another page using JavaScript as you will have to interact with the server at some point to retrieve the second page?
If you can, integrate some AJAX-request to load the second page (and parse it), but if that's not an option, I'd say you'll have to load all pages that you want to extract information from at the same time, hide the bits you don't want to show (in hidden DIVs?) and then get your index (or whoever controls the view) to retrieve the needed information from there ... even though that sounds pretty creepy ;)
You can load the page in a hidden iframe and use normal DOM manipulation to extract the results, or get the text of the page via AJAX, grab the part between <body...>...</body>¨ and temporarily inject it into a div. (The second might fail for some exotic elements like ins.) I would expect Greasemonkey to have more powerful functions than normal Javascript for stuff like that, though - it might be worth to thumb through the documentation.

Categories