Background:
I am try to fill in a pdf file that:
a) has form fields.
b) that is stored on Google Drive.
with data that is stored in Google spreadsheet.
I am using:
Google Apps Script.
HtmlService
PDF-lib.js in a htmlOutput object generated from a htmlTemplate.
The work flow is:
The showModalDialog_downloadFilledPDFform_NAMOFFORM() function is called from a menu.
The function is:
function showModalDialog_downloadFilledPDFform_NAMOFFORM() {
var pdf_template_file_url = getPDFfileURL("form1.pdf");
var htmlTemplate = HtmlService.createTemplateFromFile('downloadFilledPDFformHTML');
htmlTemplate.dataFromServerTemplate = { pdf_template_file: "form1.pdf", pdf_template_file_url: pdf_template_file_url };
var htmlOutput = htmlTemplate.evaluate();
htmlOutput.setWidth(648.1);
htmlOutput.setHeight(286.300)
SpreadsheetApp.getUi().showModalDialog(htmlOutput, 'Download filled PDF');
}
The url that is passed to the htmlTemplate is generated by: "fillPDFForm.gs"
function: fillPDFForm.gs:
var pdfFileNamesAndIDs = [ ]
pdfFileNamesAndIDs.push(["form1.pdf", "1y8F5NgnK50mdtWSR6v1b8pELsbbBJert"])
pdfFileNamesAndIDs.push(["form2.pdf", "1B4BOaI-BqFmhmnFx7FaT-yys-U0vkYKz"])
pdfFileNamesAndIDs.push(["form3.pdf", "17LrJpRA5oBZBqw-2du1H74KxWIX55qYC"])
function getPDFfileURL(fileName) {
var documentID = "";
for (var i in pdfFileNamesAndIDs) {
//console.log(pdfFileNamesAndIDs[i][0]);
if (pdfFileNamesAndIDs[i][0] == fileName) {
documentID = pdfFileNamesAndIDs[i][1];
console.log("documentID: " + documentID);
}
}
var documentFile = DriveApp.getFileById(documentID);
var documentURL = documentFile.getDownloadUrl();
Logger.log("documentURL = "+documentURL);
return documentURL;
}
The Problem:
The URL generated by getPDFfileURL() doesn't work in the html file generated in showModalDialog_downloadFilledPDFform_NAMOFFORM().
The error in Chrome dev console is:
pdf-lib#1.11.0:15 Uncaught (in promise) Error: Failed to parse PDF document (line:0 col:0 offset=0): No PDF header found
at e [as constructor] (pdf-lib#1.11.0:15:189222)
at new e (pdf-lib#1.11.0:15:190065)
at e.parseHeader (pdf-lib#1.11.0:15:401731)
at e.<anonymous> (pdf-lib#1.11.0:15:400782)
at pdf-lib#1.11.0:15:1845
at Object.next (pdf-lib#1.11.0:15:1950)
at pdf-lib#1.11.0:15:887
at new Promise (<anonymous>)
at i (pdf-lib#1.11.0:15:632)
at e.parseDocument (pdf-lib#1.11.0:15:400580)
The basic concept for the html page (shown as a modal dialog box), came from: https://jsfiddle.net/Hopding/0mwfqkv6/3/
The contents of the htmlTemplate are:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<!-- Add Stylesheet -->
<?!= HtmlService.createHtmlOutputFromFile('downloadFilledPDFformCSS').getContent(); ?>
<!-- Add pdf-lib and downloadjs libraries -->
<!-- https://pdf-lib.js.org/ and https://github.com/rndme/download -->
<!-- https://jsfiddle.net/Hopding/0mwfqkv6/3/ -->
<script src="https://unpkg.com/pdf-lib#1.11.0"></script>
<script src="https://unpkg.com/downloadjs#1.4.7"></script>
</head>
<body>
<h2 id="myTitle"></h2>
<p>Click the button to fill form fields in an the following PDF document: <code id="pdf_template_file">pdf-lib</code></p>
<button onclick="fillForm()">Fill PDF</button>
<p class="small">(Your browser will download the resulting file)</p>
</body>
<script>
const data = <?!= JSON.stringify(dataFromServerTemplate) ?>; //Stores the data directly in the javascript code
function removeExtension(filename) {
return filename.substring(0, filename.lastIndexOf('.')) || filename;
}
const pdf_template_file = data.pdf_template_file;
const pdf_template_file_basename = removeExtension(pdf_template_file);
// sample usage
function initialize() {
document.getElementById("myTitle").innerText = pdf_template_file;
//or use jquery: $("#myTitle").text(data.first + " - " + data.last);
document.getElementById("pdf_template_file").innerText = pdf_template_file;
}
// use onload or use jquery to call your initialization after the document loads
window.onload = initialize;
</script>
<script>
const { PDFDocument } = PDFLib;
async function fillForm() {
// Fetch the PDF with form fields
const formUrl = data.pdf_template_file_url
//const formPdfBytes = await fetch(formUrl).then(res => res.arrayBuffer())
const formPdfBytes = await fetch(formUrl, {
redirect: "follow",
mode: 'no-cors',
method: 'GET',
headers: {
'Content-Type': 'application/pdf',
}
}).then(res => res.arrayBuffer());
// Load a PDF with form fields
const pdfDoc = await PDFDocument.load(formPdfBytes);
// Get the form containing all the fields
const form = pdfDoc.getForm()
// Get all fields in the PDF by their names
const invIDField = form.getTextField('invID')
const makeAndModelField = form.getTextField('makeAndModel')
const nameField = form.getTextField('name')
const addressField = form.getTextField('address')
const phoneNumberField = form.getTextField('phoneNumber')
const emailAddressField = form.getTextField('emailAddress')
const dateField = form.getTextField('date')
// Output file name
const INPUT_FNAME = "AN"
const INPUT_LNAME = "Other"
// Fill in the basic info fields
invIDField.setText()
makeAndModelField.setText()
nameField.setText(INPUT_FNAME + " " + INPUT_LNAME)
addressField.setText()
phoneNumberField.setText()
emailAddressField.setText()
dateField.setText()
// Serialize the PDFDocument to bytes (a Uint8Array)
const pdfBytes = await pdfDoc.save({updateFieldAppearances: false})
const outputPDFfilename = pdf_template_file_basename + "." + INPUT_FNAME + "_" + INPUT_LNAME + ".pdf"
// Trigger the browser to download the PDF document
download(pdfBytes, outputPDFfilename, "application/pdf");
}
</script>
</html>
I have replicated the contents of the html file on my testing webserver. The server has 3 files: index.html, stykesheet.css and form1.pdf
The pdf (on the web server) is the same pdf file that is stored on Google drive.
On my server the following works:
if I use the pdf file that is in the same folder as the html and css files, a filled pdf is offered for download.
...but the following doesn't work:
if I use the same URL that is generated by getPDFfileURL(), nothing happens and no filled pdf is offered for download.
So the question is:
How do I generate the correct URL (for the pdf file stored in Google Drive), so it can then be used by PDF-lib.js (in the htmlTemplate)?
The answer is:
fetch the pdf file from Google drive as raw bytes.
encode the bytes as base64.
pass the base64 string from the GAS function to the htmlTemplate.
This is the function that gets the pdf file and returns it as a base64 encoded string:
function getPDFfileAsBase64() {
var fileId = "";
var url = "https://drive.google.com/uc?id=" + fileId + "&alt=media";
console.log("url: " + url);
var params = {
method: "get",
headers: {
Authorization: "Bearer " + ScriptApp.getOAuthToken(),
},
};
var bytes = UrlFetchApp.fetch(url, params).getContent();
var encoded = Utilities.base64Encode(bytes);
//console.log("encoded: " + encoded);
return encoded;
}
the function to create a modal dialog is:
function showModalDialog_downloadFilledPDFform_form1() {
// Display a modal dialog box with custom HtmlService content.
var pdf_template_file = "form1.pdf";
var pdf_template_file_AsBase64 = getPDFfileAsBase64(pdf_template_file);
var htmlTemplate = HtmlService.createTemplateFromFile('downloadFilledPDFformHTML');
htmlTemplate.dataFromServerTemplate = { pdf_template_file: pdf_template_file, pdf_template_file_AsBase64: pdf_template_file_AsBase64};
var htmlOutput = htmlTemplate.evaluate();
htmlOutput.setWidth(648.1);
htmlOutput.setHeight(286.300)
SpreadsheetApp.getUi().showModalDialog(htmlOutput, 'Download filled PDF');
}
Situation
I am working on an application where I can have a grid with X items, and each item has a print button. Clicking this print button allows me to call an ajax function which passes the ID of the grid item to a controller. I retrieve the relevant data based on that ID and then download it in an excel file. (The retrieving of the specific item is not yet done)
What I have so far
So far, I have the basic code that downloads an excel file, along with my grid .
Problem
The problem I am facing is, if I click the "Print" button...nothing happens, even with a breakpoint in my exporttoexcel functions shows me that the function is entered and I can step thru it and despite no errors, nothing occurs. However, I added random button that called the same function and when i clicked that button, the excel file was downloaded. As a result, I believe the issue has something to do with aJax.
Code
<input type="button" value="Test" onclick="location.href='#Url.Action("ExportToExcel", "Profile")'" />
This is the code which downloads the file. It was a simple button I added.
function ExportToExcel(id) {
$.ajax({
type: "POST",
url: "#Url.Action("ExportToExcel", "Profile")",
data: { "id": id },
dataType: "json"
});
}
This is the function that I want to work, but it does not work and I cannot see what i've got wrong.
Export to Excel Code
public void ExportToExcelx()
{
var products = new System.Data.DataTable("teste");
products.Columns.Add("col1", typeof(int));
products.Columns.Add("col2", typeof(string));
products.Rows.Add(1, "product 1");
products.Rows.Add(2, "product 2");
products.Rows.Add(3, "product 3");
products.Rows.Add(4, "product 4");
products.Rows.Add(5, "product 5");
products.Rows.Add(6, "product 6");
products.Rows.Add(7, "product 7");
var grid = new GridView();
grid.DataSource = products;
grid.DataBind();
Response.ClearContent();
Response.Buffer = true;
Response.AddHeader("content-disposition", "attachment; filename=MyExcelFile.xls");
Response.ContentType = "application/ms-excel";
Response.Charset = "";
StringWriter sw = new StringWriter();
HtmlTextWriter htw = new HtmlTextWriter(sw);
grid.RenderControl(htw);
//Response.Output.Write(sw.ToString());
//Response.Flush();
//Response.End();
// =============
//Open a memory stream that you can use to write back to the response
byte[] byteArray = Encoding.ASCII.GetBytes(sw.ToString());
MemoryStream s = new MemoryStream(byteArray);
StreamReader sr = new StreamReader(s, Encoding.ASCII);
//Write the stream back to the response
Response.Write(sr.ReadToEnd());
Response.End();
// return View("MyView");
}
Theory
I believe the error is somehow tied in to aJax, I am also creating the button in the controller like this.
"<button type='button' class='btn btn-warning' onclick='ExportToExcel(" + c.id + ");'>Print</button>",
Since location.href='#Url.Action works, I was wondering if attempting to redo my dynamic button would solve my issue.
Appreciate any insight that could be offered.
Yes you are right you have problem with ajax, Basically you have to call the controller action again from you ajax call when your first ajax call return success. Add below code snippet to your ajax call.
success: function () {
window.location = '#Url.Action("ExportExcel", "Profile")?id='+id;
}
And you have to change your controller method to return the file, as below
public FileResult ExportToExcelx()
{
...............
byte[] byteArray = Encoding.ASCII.GetBytes(sw.ToString());
return File(byteArray, System.Net.Mime.MediaTypeNames.Application.Octet, "FileName.xlsx");
}
File cannot be downloaded until full post back is triggered. Here is how you can do it:
Your ExportToExcelx function will hold file in TempData object as following:
TempData["fileHandle"] = s.ToArray();
Rather than returning view return temp data identifier "fileHandle" and file name as shown below:
return Json(new { fileHandle = "fileHandle", FileName = "file.xls" }, JsonRequestBehavior.AllowGet);
So your modified function will be like this:
public JsonResult ExportToExcelx()
{
var products = new System.Data.DataTable("teste");
products.Columns.Add("col1", typeof(int));
products.Columns.Add("col2", typeof(string));
products.Rows.Add(1, "product 1");
products.Rows.Add(2, "product 2");
products.Rows.Add(3, "product 3");
products.Rows.Add(4, "product 4");
products.Rows.Add(5, "product 5");
products.Rows.Add(6, "product 6");
products.Rows.Add(7, "product 7");
var grid = new GridView();
grid.DataSource = products;
grid.DataBind();
Response.ClearContent();
Response.Buffer = true;
Response.AddHeader("content-disposition", "attachment; filename=MyExcelFile.xls");
Response.ContentType = "application/ms-excel";
Response.Charset = "";
StringWriter sw = new StringWriter();
HtmlTextWriter htw = new HtmlTextWriter(sw);
grid.RenderControl(htw);
//Response.Output.Write(sw.ToString());
//Response.Flush();
//Response.End();
// =============
//Open a memory stream that you can use to write back to the response
byte[] byteArray = Encoding.ASCII.GetBytes(sw.ToString());
MemoryStream s = new MemoryStream(byteArray);
TempData["fileHandle"] = s.ToArray();
//StreamReader sr = new StreamReader(s, Encoding.ASCII);
//Write the stream back to the response
Response.Write(sr.ReadToEnd());
Response.End();
return Json(new { fileHandle = "fileHandle", FileName = "file.xls" }, JsonRequestBehavior.AllowGet);
// return View("MyView");
}
Now you need another function in your controller to download file like following:
[HttpGet]
public virtual ActionResult Download(string fileHandle, string fileName)
{
if (TempData[fileHandle] != null)
{
byte[] data = TempData[fileHandle] as byte[];
return File(data, "application/vnd.ms-excel", fileName);
}
else
{
return new EmptyResult();
}
}
On successful call to ExportToExcelx function your ajax call will call download function as following:
$.ajax({
type: 'GET',
cache: false,
url: '/url',
success: function (data) {
window.location = '/url/Download?fileHandle=' + data.fileHandle
+ '&filename=' + data.FileName; //call download function
},
error: function (e) {
//handle error
}
Download function then will return the file.
Hope this helps.
I've had a similar problem here, and it did solve with a dynamic button as well. I just had to include a responseType:'blob' in my request.
And get the response to the button:
var link = document.createElement('a');
link.href = window.URL.createObjectURL(response.data);
link.download='filename.xlsx';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
And my Controller writes to a output stream and produces a "application/xls"
response.setContentType("application/xls");
response.setHeader("Content-disposition", "attachment;");
response.getOutputStream().write(content);
First of all, I would not use GridView to generate excel. Despite being "easy", it won't generate an actual excel file, but rather a html file with xls extension:
<div>
<table cellspacing="0" rules="all" border="1" style="border-collapse:collapse;">
<tr>
<th scope="col">col1</th><th scope="col">col2</th>
</tr><tr>
<td>1</td><td>product 1</td>
</tr><tr>
<td>2</td><td>product 2</td>
</tr><tr>
<td>3</td><td>product 3</td>
</tr><tr>
<td>4</td><td>product 4</td>
</tr><tr>
<td>5</td><td>product 5</td>
</tr><tr>
<td>6</td><td>product 6</td>
</tr><tr>
<td>7</td><td>product 7</td>
</tr>
</table>
</div>
This results in a file that when opened will cause this:
that is pretty annoying (and unprofessional).
If you're not bounded to old excel version - xls - but can use most recent file format xlsx, I'd rather use DocumentFormat.OpenXml nuget package or other packages/libraries for excel generation.
Honestly, DocumentFormat.OpenXml is powerful but a little boring to use, when you have many columns and you just have a flat list of objects to report.
If you are using .NET Framework (not Dotnet Core), you can try
CsvHelper.Excel nuget package. Usage is pretty straight forward. Your ExportToExcel method will become something like:
public ActionResult ExportToExcel(string id)
{
// TODO: Replace with correct products retrieving logic using id input
var products = new [] {
{ col1 = 1, col2 = "product 1" },
{ col1 = 2, col2 = "product 2" },
{ col1 = 3, col2 = "product 3" },
{ col1 = 4, col2 = "product 4" },
{ col1 = 5, col2 = "product 5" },
{ col1 = 6, col2 = "product 6" },
{ col1 = 7, col2 = "product 7" },
{ col1 = 1, col2 = "product 1" },
{ col1 = 1, col2 = "product 1" },
};
var ms = new MemoryStream();
var workbook = new XLWorkbook();
using (var writer = new CsvWriter(new ExcelSerializer(workbook)))
{
writer.WriteRecords(products);
}
workbook.SaveAs(ms);
ms.Flush();
ms.Seek(0, SeekOrigin.Begin);
return File(ms, MimeMapping.GetMimeMapping("file.xlsx"), $"MyExcelFile.xlsx");
}
Another package pretty powerful is EPPlus, that allows you to load a DataTable (see this: https://stackoverflow.com/a/53957999/582792).
Coming to the AJAX part, well... I do not think you need it at all: once you set the location to the new ExportToExcel action, it should just download the file.
Assuming you are using Bootstrap 3, for each of your item in the collection you can just:
<a href="#Url.Action("ExportToExcel", "Profile", new {id=item.Id})" class="btn btn-info">
<i class="glyphicon glyphicon-download-alt" />
</a>
There are multiple solutions to this problem:
Solution 1
Let's imagine you have a Product model like this:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
In you controller:
[HttpPost]
public JsonResult ReportExcel(string id)
{
// Your Logic Here: <DB operation> on input id
// or whatsoever ...
List<Product> list = new List<Product>() {
new Product{ Id = 1, Name = "A"},
new Product{ Id = 2, Name = "B"},
new Product{ Id = 3, Name = "C"},
};
return Json(new { records = list }, JsonRequestBehavior.AllowGet);
}
Then inside your View (.cshtml), use the JSONToCSVConvertor as a utility function and just Don't touch it as it converts the array of json objects received into Excel and prompts for download.
#{
ViewBag.Title = "View export to Excel";
}
<h2>....</h2>
#* All Your View Content goes here *#
#* This is a sample form *#
<form>
<div class="form-group">
<label>Product ID</label>
<div class="col-md-10">
<input id="productID" name="productID" class="form-control"/>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input id="submit" type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</form>
#section scripts{
<script>
$('#submit').click(function (e) {
e.preventDefault();
var ID = $('#productID').val();
$.ajax({
cache: false,
type: 'POST',
url: '/YourControllerName/ReportExcel',
data: {id: ID},
success: function (data) {
console.log(data);
JSONToCSVConvertor(data.records, "Sample Report", true);
}
})
});
function JSONToCSVConvertor(JSONData, ReportTitle, ShowLabel) {
//If JSONData is not an object then JSON.parse will parse the JSON string in an Object
var arrData = typeof JSONData != 'object' ? JSON.parse(JSONData) : JSONData;
var CSV = 'sep=,' + '\r\n\n';
//This condition will generate the Label/Header
if (ShowLabel) {
var row = "";
//This loop will extract the label from 1st index of on array
for (var index in arrData[0]) {
//Now convert each value to string and comma-seprated
row += index + ',';
}
row = row.slice(0, -1);
//append Label row with line break
CSV += row + '\r\n';
}
//1st loop is to extract each row
for (var i = 0; i < arrData.length; i++) {
var row = "";
//2nd loop will extract each column and convert it in string comma-seprated
for (var index in arrData[i]) {
row += '"' + arrData[i][index] + '",';
}
row.slice(0, row.length - 1);
//add a line break after each row
CSV += row + '\r\n';
}
if (CSV == '') {
alert("Invalid data");
return;
}
//Generate a file name
var fileName = "MyReport_";
//this will remove the blank-spaces from the title and replace it with an underscore
fileName += ReportTitle.replace(/ /g, "_");
//Initialize file format you want csv or xls
var uri = 'data:text/csv;charset=utf-8,' + escape(CSV);
// Now the little tricky part.
// you can use either>> window.open(uri);
// but this will not work in some browsers
// or you will not get the correct file extension
//this trick will generate a temp <a /> tag
var link = document.createElement("a");
link.href = uri;
//set the visibility hidden so it will not effect on your web-layout
link.style = "visibility:hidden";
link.download = fileName + ".csv";
//this part will append the anchor tag and remove it after automatic click
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
</script>
}
I ran and built the above code successfully, so feel free to grab and tweak it as you wish.
Also here is the jsFiddle link, thanks to its developer: https://jsfiddle.net/1ecj1rtz/
Solution 2
Call this action method through $.ajax and get the file downloaded:
public FileResult Export(int id)
{
//......... create the physical file ....//
byte[] fileBytes = File.ReadAllBytes(filePath);
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}
Along with Solution 2, this thread gives you a good idea: https://stackoverflow.com/a/16670517/4687359
Hope this helped. :)
ANSWER:
You need to include success in your ajax call.
function ExportToExcel(id) {
$.ajax({
type: "POST",
url: "#Url.Action("ExportToExcel", "Profile")",
data: { "id": id },
dataType: "json"
success: function () {
window.location = '#Url.Action("ExportExcel", "Profile")?id='+id;
}
});
}
For Duplicate headers received from the server
Duplicate headers received from server
The data will be transformed to a query string on an AJAX GET request; just do that yourself using the jQuery param function:
$('#excel').on('click',function(){
var query = {
location: $('#location').val(),
area: $('#area').val(),
booth: $('#booth').val()
}
var url = "{{URL::to('downloadExcel_location_details')}}?" + $.param(query)
window.location = url;
});
Here is how I got this working for PDF. Excel download should be similar
$.ajax({
url: '<URL_TO_FILE>',
success: function(data) {
var blob=new Blob([data]);
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download="<FILENAME_TO_SAVE_WITH_EXTENSION>";
link.click();
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
OR using download.js
$.ajax({
url: '<URL_TO_FILE>',
success: download.bind(true, "<FILENAME_TO_SAVE_WITH_EXTENSION>", "<FILE_MIME_TYPE>")
});
I have a from with input of type file I need to get the file data from this form without refreshing the page
I'm try to use this function
function submitForm(form){
var url = $(form).attr("action");
var formData = {};
$(form).find("input[name]").each(function (index, node) {
formData[node.name] = node.value;
});
$.post(url, formData).done(function (data) {
alert(data);
});
}
but this function get the values of form inputs, but I need to get all file data (tmp_name, file_name, file_type ...)
so can any one help me in this please
thanks in advance
Maybe you can reference your input of type file by id and then get the files property to obtain information about the files.
Then you can loop through the files and for example read the name, size and type attribute of the File object.
https://developer.mozilla.org/en-US/docs/Using_files_from_web_applications
https://developer.mozilla.org/en-US/docs/Web/API/File
For example
$("#theForm").submit(function(e) {
submitForm(this);
e.preventDefault();
});
function submitForm(form) {
var url = $(form).attr("action");
var formData = {};
formData.filesInfo = [];
var files = $('#inputFile').prop("files");
$(files).each(function() {
var fileInfo = {};
fileInfo.name = this.name;
fileInfo.size = this.size;
fileInfo.type = this.type;
formData.filesInfo.push(fileInfo);
});
$.post(url, formData).done(function (data) {
alert(data);
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form id="theForm">
<input type="file" id="inputFile">
<input type="submit" name="submit" id="submit">
</form>
I have this JS code for uploading a .jpg file to firebase:
function handleFileSelect(evt) {
var f = evt.target.files[0];
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
var filePayload = e.target.result;
// Generate a location that can't be guessed using the file's contents and a random number
var hash = CryptoJS.SHA256(Math.random() + CryptoJS.SHA256(filePayload));
var f = new Firebase(firebaseRef + 'pano/' + hash + '/filePayload');
spinner.spin(document.getElementById('spin'));
// Set the file payload to Firebase and register an onComplete handler to stop the spinner and show the preview
f.set(filePayload, function() {
spinner.stop();
document.getElementById("pano").src = e.target.result;
$('#file-upload').hide();
// Update the location bar so the URL can be shared with others
window.location.hash = hash;
});
};
})(f);
reader.readAsDataURL(f);
}
$(function() {
$('#spin').append(spinner);
var idx = window.location.href.indexOf('#');
var hash = (idx > 0) ? window.location.href.slice(idx + 1) : '';
if (hash === '') {
// No hash found, so render the file upload button.
$('#file-upload').show();
document.getElementById("file-upload").addEventListener('change', handleFileSelect, false);
} else {
// A hash was passed in, so let's retrieve and render it.
spinner.spin(document.getElementById('spin'));
var f = new Firebase(firebaseRef + '/pano/' + hash + '/filePayload');
f.once('value', function(snap) {
var payload = snap.val();
if (payload != null) {
document.getElementById("pano").src = payload;
} else {
$('#body').append("Not found");
}
spinner.stop();
});
}
});
My js imports:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="//cdn.firebase.com/v0/firebase.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/spin.js/1.2.7/spin.min.js"></script>
<script src="sha256.js"></script>
<script src="firepano.js"></script>
My html looks something like this:
<input type="file" accept="image/*" capture="camera" id="file-upload">
Of course this is just a piece, but somehow the js function isn't triggered when I select a file, only if I put the HTML code outside of my file in a separate file and leave only this input tag. Is there some other piece of code that can interfier in the selectfile function trigger?
Try this:
<form action="" method="post" enctype="multipart/form-data">
<input type="file" name="image">
<input type="submit" value="Upload">
</form>
<!doctype html>
<html>
<head>
<title>Twitter</title>
<meta charset="utf-8">
<script>
window.onload = function () {
// set up the click handler for the form button
var button = document.getElementById("submit");
button.onclick = getTweets;
}
// when you click "Get Tweets" we call this function
function getTweets() {
// set up a new XHR request
var xhr = new XMLHttpRequest();
// we're calling search.php and passing in a query string
var url = "search.php?query=";
var query = document.getElementById("query").value;
if (!query) {
query = "html5";
}
// we encode the query to handle any special characters properly
url += encodeURIComponent(query);
// this is the function that is called when the XHR request
// to our search.php script is handled, and a response sent back
xhr.onload = function () {
// if everything went okay, then send the response data
// to the displayTweets() function
if (xhr.status == 200) {
displayTweets(xhr.responseText);
} else {
var errorDiv = document.getElementById("error");
errorDiv.innerHTML = "Error getting tweets: " + xhr.status;
}
};
// make the request!
xhr.open("GET", url);
xhr.send(null);
}
function displayTweets(tweets) {
// tweets is a big long string, so we need to parse it
// into JSON first
tweets = JSON.parse(tweets);
var ul = document.querySelector("ul");
// clear existing tweets from list
while (ul.hasChildNodes()) {
ul.removeChild(ul.lastChild);
}
// add new tweets
for (var i = 0; i < tweets.length; i++) {
var li = document.createElement("li");
li.innerHTML = tweets[i].tweet;
ul.appendChild(li);
}
}
</script>
</head>
<body>
<form>
Query:
<input type="text" id="query">
<input type="button" id="submit" value="Get Tweets">
</form>
<div id="error"></div>
<ul></ul>
</body>
</html>
In the above code when I enter some text in the textbox and click on "Get Tweets" button it gives an error as Error getting tweets: 0. The search.php when executed independently without embedding in html and javascript gives accurate and required results.Can you please check the html and js code and suggest any changes in code??
Seems that the issue is CROSS-DOMAIN Ajax issue. When you execute the search.php independently, then there is no X-domain issue. But when you are embedding the code in some html, check if the html is part of the same domain or not. Also, if you are trying to run the html file from file:/// it will not work.