I want to embed a specific range of cells from a Google Docs spreadsheet (or if that doesn't work the whole sheet without header and toolbar) in a website, so that any website visitor can input their data.
I know how to embed a specific range of cells an html table, but that doesn't allow website users to edit the sheet.
A similar question was asked several years ago here but the suggested solution doesn't seem to work anymore.
I called this the htmlSpreadsheet you can use as a dialog or deploy as webapp the doGet is already to go. It's still a pretty simple app. There's a lot of room for customization.
Here's the code.gs file:
function onOpen()
{
SpreadsheetApp.getUi().createMenu('HTML Spreadsheet')
.addItem('Run Spreadsheet in Dialog', 'htmlSpreadsheet')
.addToUi();
}
var SSID='SpreadSheetID';
var sheetName='Sheet1';
function htmlSpreadsheet(mode)
{
var mode=(typeof(mode)!='undefined')?mode:'dialog';
var br='<br />';
var s='';
var hdrRows=1;
var ss=SpreadsheetApp.openById(SSID);
var sht=ss.getSheetByName(sheetName);
var rng=sht.getDataRange();
var rngA=rng.getValues();
s+='<table>';
for(var i=0;i<rngA.length;i++)
{
s+='<tr>';
for(var j=0;j<rngA[i].length;j++)
{
if(i<hdrRows)
{
s+='<th id="cell' + i + j + '">' + '<input id="txt' + i + j + '" type="text" value="' + rngA[i][j] + '" size="10" onChange="updateSS(' + i + ',' + j + ');" />' + '</th>';
}
else
{
s+='<td id="cell' + i + j + '">' + '<input id="txt' + i + j + '" type="text" value="' + rngA[i][j] + '" size="10" onChange="updateSS(' + i + ',' + j + ');" />' + '</th>';
}
}
s+='</tr>';
}
s+='</table>';
//s+='<div id="success"></div>';
s+='</body></html>';
switch (mode)
{
case 'dialog':
var userInterface=HtmlService.createHtmlOutputFromFile('htmlss').setWidth(1000).setHeight(450);
userInterface.append(s);
SpreadsheetApp.getUi().showModelessDialog(userInterface, 'Spreadsheet Data for ' + ss.getName() + ' Sheet: ' + sht.getName());
break;
case 'web':
var userInterface=HtmlService.createHtmlOutputFromFile('htmlss').setWidth(1000).setHeight(450);
return userInterface.append(s).setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
}
function updateSpreadsheet(i,j,value)
{
var ss=SpreadsheetApp.openById(SSID);
var sht=ss.getSheetByName(sheetName);
var rng=sht.getDataRange();
var rngA=rng.getValues();
rngA[i][j]=value;
rng.setValues(rngA);
var data = {'message':'Cell[' + Number(i + 1) + '][' + Number(j + 1) + '] Has been updated', 'ridx': i, 'cidx': j};
return data;
}
function doGet()
{
var output=htmlSpreadsheet('web');
return output;
}
This is the htmlss.html file:
<!DOCTYPE html>
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
$(function() {
});
function updateSS(i,j)
{
var str='#txt' + String(i) + String(j);
var value=$(str).val();
$(str).css('background-color','#ffff00');
google.script.run
.withSuccessHandler(successHandler)
.updateSpreadsheet(i,j,value)
}
function successHandler(data)
{
$('#success').text(data.message);
$('#txt' + data.ridx + data.cidx).css('background-color','#ffffff');
}
console.log('My Code');
</script>
<style>
th{text-align:left}
</style>
</head>
<body>
<div id="success"></div>
Following post will be helpfull:
https://support.google.com/docs/answer/37579?hl=fr&ref_topic=2818998
And with some added value, the following is the template you may want to try, where ------ is specific code to your sheet
<iframe style="border: 0;" src="https://docs.google.com/spreadsheets/-----/pubhtml?gid=--------&range=C2:E&chrome=false&single=true&widget=false&headers=false" width="800" height="750" frameborder="0" scrolling="yes"><span data-mce-type="bookmark" style="display: inline-block; width: 0px; overflow: hidden; line-height: 0;" class="mce_SELRES_start"></span><span data-mce-type="bookmark" style="display: inline-block; width: 0px; overflow: hidden; line-height: 0;" class="mce_SELRES_start"></span><span data-mce-type="bookmark" style="display: inline-block; width: 0px; overflow: hidden; line-height: 0;" class="mce_SELRES_start"></span></iframe>
I am pulling blog posts in from the blogger JSON api into my page. The posts are HTML formatted. Some of the posts have floated content; an image, for instance, that is floated left with text to the right that is shorter than the image. The title and date of the post below it are pushed to the right as well. I remember there being an esoteric way within CSS to isolate float within a div. I can't remember how. And I don't remember what it is even called. I've been searching all day. Any ideas?
<!DOCTYPE html>
<html lang="en">
<head>
<title>Sandy Reads - News</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<style></style>
</head>
<body>
<div class="container-fluid">
<section id="posts">
<div class="page-header">
<h1>Sandy Reads <small>News</small></h1>
</div>
</section>
</div>
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.6/moment-with-locales.min.js"></script>
<script>
$(function () {
var key = "Redacted";
var blogId = "Redacted";
var resource = "https://www.googleapis.com/blogger/v3/blogs/" + blogId + "/posts?maxResults=10&key=" + key;
$.getJSON(resource, function (data) {
var items = [];
$.each(data.items, function (key, val) {
var kind = val.kind;
if (kind !== 'blogger#post')
return;
var blogId = val.id;
var title = val.title;
var content = val.content;
var date = moment(val.updated);
items.push("<li id='" + blogId + "' class='post'>" +
"<a href='blog.html?id=" + blogId + "'><h3>" + title + "</h3></a>" +
"<i>" + date.format('MMMM Do YYYY') + "</i></div>" +
"<div>" + content + "</div>" +
"</li>");
});
$("<ul/>", {
"class": "list-group my-new-list",
html: items.join("")
}).appendTo("#posts");
});
});
</script>
</body>
</html>
You're probably thinking of overflow: hidden, but which element you apply it to depends on what your markup looks like. I can tell you to apply it to the parent of the float, but if the content that is being pushed aside appears in this same parent, then that's not going to help.
OK, so your script generates posts in a series of li.post elements. The content appears in a div following the title and date. You can either set li.post to clear floats, or apply overflow: hidden to the div. (There is a spurious </div> end tag after your post date that you may want to account for.)
We are using Rotativa to save a permanent PDF copy of the page on the web server. Here is the ASP.NET MVC 4 code to build and save it:
var pdfResult = new ActionAsPdf("Index", new { orderId = orderValidated.orderID }) { FileName = filePath };
var binary = pdfResult.BuildPdf(ControllerContext);
// writes only if the file does not exist, we need to keep first authentic print of it.
if (System.IO.File.Exists(filePath) == false)
{
System.IO.File.WriteAllBytes(filePath, binary);
}
We use a specific 'css' file for printing in the page <head> section:
<head>
<link media=all href="/Content/invoice.css" type=text/css rel=stylesheet>
<link media=print href="/Content/printInv.css" type=text/css rel=stylesheet>
</head>
The 'css' print version contains:
#logo {background: url(/assets/images/logo-plain.gif) no-repeat; height: 56px}
and the *.gif image is missing from the PDF document (on screen is present).
Any idea on the reason why it's missing?
Thanks.
I'm think this is an issue with Rotativa.
What I did to solve was insert the background image inline using the image tag then use css to position it:
#bg {
position: absolute;
top: 8px;
left: 0px;
z-index: 0;
}
<img src="#Url.Content("~/content/background-image.jpg")" id="bg" />
As far as I know, Rotativa has problems with gif images. Try using jpg or png.
"<img height=200 width=200 src="/ProductImages/" + rd["ItemCode"].ToString() + ".jpg" />"
hi everybody I'm trying to make a Random Generator of Discourses but I don't want to refresh the entire page in order to get a new string, so I thought that maybe innerHTML could be my solution. Unfortunately It gives me an "UNDEFINED" result when I click on the button. I don't know how to proceed. Thanks in advance.
<span id="generator" style="font-size: 20px; font-weight: bold;">
<script language="javascript" type="text/javascript">
Words1 = new Array("blablabla1","blablabla2")
Words2 = new Array("blablabla3","blablabla4")
var random = document.write('- ' +
Words1[Math.floor((Math.random() * 100000) % Words1.length)] +
' ' +
Words2[Math.floor((Math.random() * 100000) % Words2.length)] +
'.\n' );
function refresh(){
document.getElementById('generator').innerHTML = random;
}
</script>
</span>
<input type='button' onclick='refresh()' value='refresh'/>
You are wrapping the javascript code with the span tag. Should be like this and put the code inside de function:
<span id="generator" style="font-size: 20px; font-weight: bold;"></span>
<script language="javascript" type="text/javascript">
function refresh(){
Words1 = new Array("blablabla1","blablabla2")
Words2 = new Array("blablabla3","blablabla4")
var random = document.write('- ' +
Words1[Math.floor((Math.random() * 100000) % Words1.length)] +' ' + Words2[Math.floor((Math.random() * 100000) % Words2.length)] +'.\n' );
document.getElementById('generator').innerHTML = random;
}
</script>
<input type='button' onclick='refresh()' value='refresh'/>
I have used Version 2 for quite some time and decided it was time to go to Version 3 as part of a major site update. I based all my code on "Mike Williams' tutorial The Basics - Part 3: Loading the data from an XML file translated to v3". After hours of fine-tuning I had it all working, including clustering and custom markers.
I was a very happy bunny and keen to show it off. Then to my disappointment I discovered that it didn't work in either IE or Firefox (I had been testing it in Chrome).
I'm well aware that Google Maps API is not an easy thing to work with, but is there any reason why I should get this behaviour with different browsers? The map can be seen by going to www.littlehotels.co.uk/new/ and clicking on "Map Search". The code is here:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>Little Hotels of Spain - Google Map</title>
<meta name="description" content="Little Hotels provides maps showing the location of hotels, using Google Maps to create both a street/road map and a satellite image.">
<meta name="keywords" content="Little Hotels, Little Hotels of Spain, Spain, mainland spain, balearic, balearics, canary, canaries, small, hotel, hotels, map, google map, holiday, holidays">
<style type="text/css">
html { height: 100% }
body{ height: 100%; margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; font-size:12px; color:#333333;}
a:link, a:visited, a:hover {color: #FF6600; text-decoration: none; font-weight: bold;}
h1{font-size: 16px; color: #2B8CB9; font-weight: bold;}
#content{padding: 0 5px 0; width: 640px;}
#map_canvas { height: 100% }
.verdana{font-family:Verdana, Arial, Helvetica, sans-serif; font-size: 9px; color: #666666;}
</style>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/src/markerclusterer_compiled.js"></script>
<script type="text/javascript" src="../js/downloadxml.js"></script>
<script type="text/javascript">
//<![CDATA[
var gmarkers = [];
// global "map" variable
var map = null;
var markerclusterer = null;
var image = new google.maps.MarkerImage('../images/hotel_icon.png',
new google.maps.Size(32, 37),
new google.maps.Point(0,0),
new google.maps.Point(16, 35));
var shadow = new google.maps.MarkerImage('../images/hotel_shadow.png',
new google.maps.Size(51, 37),
new google.maps.Point(0,0),
new google.maps.Point(16, 35));
// A function to create the marker and set up the event window function
function createMarker(latlng, name, html) {
var contentString = html;
var marker = new google.maps.Marker({
position: latlng,
icon: image,
shadow: shadow
});
google.maps.event.addListener(marker, 'click', function() {
infowindow.setContent(contentString);
infowindow.open(map,marker);
});
// save the info we need to use later for the side_bar
gmarkers.push(marker);
}
// This function picks up the click and opens the corresponding info window
function myclick(i) {
google.maps.event.trigger(gmarkers[i], "click");
}
function initialize() {
var lat = 0;
var lng = 0;
var zoomzoom = 0;
var query = location.search.substring(1);
var pairs = query.split("&");
for (var i=0; i<pairs.length; i++) {
var pos = pairs[i].indexOf("=");
var argname = pairs[i].substring(0,pos).toLowerCase();
var value = pairs[i].substring(pos+1).toLowerCase();
if (argname == "lat") {lat = parseFloat(value);}
if (argname == "lng") {lng = parseFloat(value);}
if (argname == "zoom") {zoomzoom = parseInt(value);}
}
var thisLatlng = new google.maps.LatLng(lat, lng);
var myOptions = {
center: thisLatlng,
zoom: zoomzoom,
mapTypeId: google.maps.MapTypeId.ROADMAP,
streetViewControl: false,
zoomControl: true,
zoomControlOptions: {
style: google.maps.ZoomControlStyle.SMALL
}
}
map = new google.maps.Map(document.getElementById("map_canvas"),
myOptions);
google.maps.event.addListener(map, 'click', function() {
infowindow.close();
});
downloadUrl("php-to-xml.php", function(doc) {
var xmlDoc = xmlParse(doc);
var markers = xmlDoc.documentElement.getElementsByTagName("marker");
for (var i = 0; i < markers.length; i++) {
var lat = parseFloat(markers[i].getAttribute("lat"));
var lng = parseFloat(markers[i].getAttribute("lng"));
var point = new google.maps.LatLng(lat,lng);
var html=markers[i].getAttribute("html");
var hotel=markers[i].getAttribute("hotel");
var marker = createMarker(point,hotel,html);
}
var clusterStyles = [
{
opt_textColor: '#5a7aba',
url: '../images/cluster_icon.png',
height: 40,
width: 40
}
];
var mcOptions = {gridSize: 35, maxZoom: 8, styles: clusterStyles};
markerCluster = new MarkerClusterer(map, gmarkers,mcOptions);
});
}
var infowindow = new google.maps.InfoWindow(
{
size: new google.maps.Size(300,50)
});
//]]>
</script>
</head>
<body onload="initialize()">
<div id="content">
<h1>Little Hotels Map Search</h1>
<span class="verdana">Click on the icons for more information or use the Google controls to zoom, scroll, pan and change view.<br /><br /></span>
<table border=1 bordercolor="#666666">
<tr>
<td>
<div id="map_canvas" style="width: 640px; height: 450px"></div>
</td>
</tr>
</table>
<br />
</div>
</body>
</html>
Thanks for any advice anyone can offer.
I get the error in Firefox:
Error: not well-formed
Source File: http://www.littlehotels.co.uk/new/maps/php-to-xml.php
Line: 1, Column: 1722
Check with an XML validator.
The returned XML is not well-formed. It contains :
pretty "pueblos blancos"
you have to use " for the double-quotes or a CDATA-section.
Because of that you'll not be able to parse the returned data as XML
Dr Molle, this is the PHP script:
<?php
header('Content-Type: text/xml');
header('charset:UTF-8');
require_once('../Connections/MySQL_extranet.php');
function parseToXML($htmlStr)
{
$xmlStr=str_replace('<','<',$htmlStr);
$xmlStr=str_replace('>','>',$xmlStr);
$xmlStr=str_replace('"','"',$xmlStr);
$xmlStr=str_replace("'",''',$xmlStr);
$xmlStr=str_replace("&",'&',$xmlStr);
$xmlStr=str_replace("ñ",'ñ',$xmlStr);
return $xmlStr;
}
mysql_select_db($database_MySQL_extranet, $MySQL_extranet);
// Select all the rows in the markers table
$query = "SELECT hid, hotel, lat, lng, picture, summary FROM ex_hotel WHERE country = 'spain'";
$result = mysql_query($query);
if (!$result) {
die('Invalid query: ' . mysql_error());
}
// Start XML file, echo parent node
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
echo '<markers>';
// Iterate through the rows, printing XML nodes for each
while ($row = #mysql_fetch_assoc($result)){
// ADD TO XML DOCUMENT NODE
echo '<marker ';
echo ' html="<img src="../images/' . $row['picture'] . '" align="left" height="108" width="155" style="margin-right:8px;"> <div style="width:320px"> <a href="' . 'http://www.littlehotels.co.uk/new/spain/' . $row['hid'] . '.php" target="_parent" >' . parseToXML($row['hotel']) . '</a> <br><span class="verdana"><br>'.parseToXML($row['summary']).'</span></div>"';
echo ' lat="' . $row['lat'] . '"';
echo ' lng="' . $row['lng'] . '"';
echo ' />';
}
// End XML file
echo '</markers>';
?>
The parsetoXML function is supposed to solve my original problem. It works on quotes but not on the ñ character. I have variously tried replacing ñ with ñ &0141; and n. None of them work. However, I've just settled for avoiding use of ñ at the moment. I'll come back to address this problem again when I have more time.
Many thanks for pointing me in the right direction.
've just been looking into the same issue. It seems the custom icon shadow feature is being dropped anyway.
"All shadows have been removed in the visual refresh. Any shadows specified programmatically will be ignored."
https://developers.google.com/maps/documentation/javascript/basics#VisualRefreshChanges