Cannot update text in paragraph of NamedRange - google-apps-script

I have a radio button with a few options in my google Doc. When the user selects an option, it adds a section with a heading and also paragraph. It should add the section only if the section isn't available and append the 'updated' text to the paragraph.
If the section is already available, it should only append the updated text at the end of the text.
Issue: Actually the behavior is pretty strange:
For instance, if I select the section named 'Diagnoses' it will be created correctly.
Diagnoses
updated text, new range? true
If I select the section named 'Anamnese'. the section will be also created correctly:
Diagnoses
updated text, new range? false
Anamnese
updated text, new range? true
If I switch back to 'Diagnoses' it looks like this:
Diagnose
updated text, new range? true
Anamneseupdated text, new range? false
updated text, new range? trueupdated text, new range? false
function manageSection(selectedSection) {
section = selectedSection;
var range = doc.getNamedRanges().find(r => r.getName() == section);
if (!range) {
Logger.log('No named range. Installing a named range');
setNamedRange(section)
} else {
Logger.log('Named range found.');
getNamedRange(section)
}
}
// No namedRange found - Set new named range with name section.
function setNamedRange(section) {
goToLastLine();
// Append a section header
var s = body.appendParagraph(section);
s.setHeading(DocumentApp.ParagraphHeading.HEADING4);
// Append paragraph to section
var insert = body.appendParagraph('');
var rangeBuilder = doc.newRange();
rangeBuilder.addElement(insert);
var savedInsert = rangeBuilder.build()
var namedRange = doc.addNamedRange(section, savedInsert);
var namedRangeId = namedRange.getId();
// select the namedRange
doc.setSelection(doc.getNamedRangeById(namedRangeId).getRange());
var newRange = true;
changeTextByNamedRange(section, newRange)
}
// Existing namedRange found.
function getNamedRange(section) {
Logger.log('getNamedRange() started')
var newRange = false;
changeTextByNamedRange(section, newRange)
}
// Update named range.
function changeTextByNamedRange(section, newRange) {
docUi.alert('section:' + section);
var range = doc.getNamedRanges().find(r => r.getName() == section);
// test
var updateText = "updated text, new range? " + newRange;
//
range.getRange().getRangeElements().forEach(e => e.getElement().asText().appendText(updateText));
}
function goToLastLine(){
const kids = body.getNumChildren()
const lastKid = body.getChild(kids - 1)
let last = 0
try {
const lastPar = body.getChild(kids - 1).asParagraph()
last = doc.newPosition(lastPar.getChild(0), lastPar.getText().length)
} catch (e) {
last = doc.newPosition(body.getChild(kids - 1), 0)
} finally {
doc.setCursor(last)
}
}

I have a workaround working and feel free to modify if it still is within your goal.
I modified setNamedRange and changeTextByNamedRange functions. Here are their modifications:
setNamedRange:
function setNamedRange(section) {
// Append a section header
goToLastLine();
var s = body.appendParagraph(section);
s.setHeading(DocumentApp.ParagraphHeading.HEADING4);
// Append paragraph to section
// for some reason, it fails to include the 1st paragraph when it is blank
// you can add a space, or any character just to include the 1st one properly
var insert = body.appendParagraph("\t");
var rangeBuilder = doc.newRange();
rangeBuilder.addElement(insert);
var savedInsert = rangeBuilder.build();
var namedRange = doc.addNamedRange(section, savedInsert);
var namedRangeId = namedRange.getId();
// select the namedRange
doc.setSelection(doc.getNamedRangeById(namedRangeId).getRange());
changeTextByNamedRange(section);
}
changeTextByNamedRange:
function changeTextByNamedRange(section) {
// docUi.alert('section:' + section);
var range = doc.getNamedRanges().find(r => r.getName() == section);
// get list of section names
var sectionNames = doc.getNamedRanges().map(r => r.getName());
var updateText = "Newer Text is appended for this section " + section + ". ";
// this section contains the succeeding sections as well
// so you have to end the loop when you encounter another section
// use some instead of forEach to be able to break the loop.
range.getRange().getRangeElements().some(e => {
var element = e.getElement();
// do anything you want. Note that this will loop to each element of that range.
// if section has multiple elements, it will append to each element.
// since we are only appending text to a single paragraph element, this currently works
element.asText().appendText(updateText)
// end loop when you encounter a text that is one of the section names
return !sectionNames.includes(element.asText())
});
}
Output:
After multiple runs on different sections, modifying the text to be appended, added some manual text then running manageSection again. This is the result.

Related

Google Apps Script document body doesn't include content from batchUpdate API call

I'm trying to create a script that adds section breaks before any Heading 1 in a Google doc, and that part is working: Before, After
However, I would like to continue to do other things within the script as well, such as delete blank lines. I found out by printing out all the elements in the body of the document that the new elements from the batch update (section break and a blank line inserted with it) are not included. Using my function findType(), it prints "Paragraph" 3x rather than "Paragraph" 2x, "Unsupported", "Paragraph" 2x I cannot figure out why it does not see the new elements.
My code:
function insertSectionBreaks() {
let link = 'link to doc';
var doc = DocumentApp.openByUrl(link);
const documentId = doc.getId();
var body = doc.getBody();
// Define the search parameters.
var searchType = DocumentApp.ElementType.PARAGRAPH;
var searchHeading = DocumentApp.ParagraphHeading.HEADING1;
var searchResult = null;
var childIndex = null;
var docsObj = null;
var resource = {requests: []};
// Search until the paragraph is found.
while (searchResult = body.findElement(searchType, searchResult)) {
var paragraph = searchResult.getElement().asParagraph();
if (paragraph.getHeading() == searchHeading) {
//Here is where a heading is
childIndex = body.getChildIndex(paragraph);
docObj = Docs.Documents.get(documentId).body.content
resource.requests.push(
{insertSectionBreak: {
sectionType: "CONTINUOUS",
//location: {index: childIndex}
location: {index: docObj[childIndex+1].startIndex + resource.requests.length*2}//Multiply by two bc it adds a line for the section and a blank line on top
}}
);
}
}
Docs.Documents.batchUpdate(resource, documentId)
body = DocumentApp.openById(documentId);
for(var i = 0; i < body.getNumChildren(); i++){
findType(body.getChild(i));
}
}
function findType(element){
switch(element.getType()) {
case DocumentApp.ElementType.PARAGRAPH:
console.log("Paragraph");
return DocumentApp.ElementType.PARAGRAPH
break;
case DocumentApp.ElementType.UNSUPPORTED:
console.log("Unsupported");
return DocumentApp.ElementType.UNSUPPORTED
break;
default:
console.log("NONE");
return null;
}
}
I would really appreciate any help.

Apps Script - Google Form to Email PDF - Need Checkbox questions to generate as Bullet Points

When Google Form is submitted, it finds the items to replace in the Google Doc template, then emails to me.
I am having trouble getting the Form checkbox Response as bullet points in the Doc. It currently will list all options that were checked but they are separated by commas, not bullet points.
Any help on this would be really appreciated!
function onFormSubmit(e) {
//open the template document by ID
//you can find the ID in the link of the document
var templateDoc = DriveApp.getFileById('1Dwbjh1Jqerg_WKvqdERyCfnDnWY2yjNArGY_f38ioAs');
//create a copy of the template, we don't wanna mess up the template doc
var newTempFile = templateDoc.makeCopy();
//open the new template document for editing
var openDoc = DocumentApp.openById(newTempFile.getId());
var body = openDoc.getBody();
//get the responses triggered by On Form Submit
var items = e.response.getItemResponses();
//find the text in the template doc and replace it with the Form response
//items[0].getResponse() is the first response in the Form
//and it is the "Name"
body.replaceText('{Position}', items[0].getResponse());
//items[1].getResponse() is the second and it is the date
body.replaceText('{Location}', items[1].getResponse());
//You can add as much as you have and change them in the Template Doc like this
body.replaceText('{Days/Hours}', items[2].getResponse());
body.replaceText('{Qualifications}', items[3].getResponse());
body.replaceText('{Duties}', items[4].getResponse());
//and so on...
//Save and Close the open document
openDoc.saveAndClose();
var theBlob = newTempFile.getBlob().getAs('application/pdf');
//The name of the file is going to be the first and second question from the form
//change to your preference
var nameFile = items[0].getResponse() + '-' + items[1].getResponse() + '.pdf';
//send an email with the PDF
//If you don't want to send the PDF in the mail just delete everything
//from here -------
var email = 'youremail#email.com';
var subject = 'Your new document';
var body = 'Hello, <br/>Check this PDF file.';
GmailApp.sendEmail(email, subject, body, {
htmlBody: body,
attachments: [{
fileName: nameFile,
content: theBlob.getBytes(),
mimeType: "application/pdf"
}]
});
//to here ------
// save the PDF file in your Drive
var savePDF = DriveApp.createFile (theBlob);
//if you want to save the file in a specific folder use this code
//get the ID from the folder link
//var folder = DriveApp.getFolderById('14nUc----------0lUb');
//var savePDF = folder.createFile (theBlob);
savePDF.setName(nameFile);
//delete the temp file
DriveApp.getFileById(newTempFile.getId()).setTrashed(true);
}
You can't directly replace a text with multiple bullets.
First, you need to find where the element is. And instead of replacing text directly, you need to traverse the elements returned by the checkbox response and then append them one by one below the list/bullet containing the placeholder text. After appending all the elements, remove the placeholder text and its bullet.
To do those, I've added two separate functions. First is for appending new list items to a placeholder bullet and the other one is for removing the placeholder bullet after adding all those new list items. See code below.
Code:
// make sure to copy the original template, and use that copy instead of directly editing the original template which what I did below
var templateDoc = DriveApp.getFileById('1Ep2yDj-EZohCQAZl-TV1iiFqfTdHulffAiHJWH_8YFY');
var openDoc = DocumentApp.openById(templateDoc.getId());
var body = openDoc.getBody();
function onFormSubmit(e) {
var items = e.response.getItemResponses();
items.forEach(function (item, index) {
switch (index){
case 0:
body.replaceText('{Position}', item.getResponse());
break;
case 1:
body.replaceText('{Location}', item.getResponse());
break;
case 2:
body.replaceText('{Days/Hours}', item.getResponse());
break;
case 3:
replaceBullet('{Qualifications}', item);
break;
case 4:
replaceBullet('{Duties}', item);
break;
default:
break;
}
});
// Now, we finished replacing the values we need to be replaced
// Add here the saving and closing of the document.
// Then prepare your send email code here.
}
function replaceBullet(template, item) {
var choices = item.getResponse();
choices.forEach(function (choice) {
appendToList(choice, template);
});
removeListTemplate(template);
}
function appendToList(elementContent, template) {
var childIndex = 0;
for (var i = 0; i < openDoc.getNumChildren(); i++) {
var child = openDoc.getChild(i);
if (child.getType() == DocumentApp.ElementType.LIST_ITEM && child.asText().getText() == template){
while(child.getType() == DocumentApp.ElementType.LIST_ITEM){
child = openDoc.getChild(i);
childIndex = body.getChildIndex(child);
i++;
}
child = openDoc.getChild(i-2);
var newElement = child.getParent().insertListItem(childIndex, elementContent).setGlyphType(DocumentApp.GlyphType.BULLET);
newElement.setListId(child);
}
}
}
function removeListTemplate(template) {
for (var i = 0; i < openDoc.getNumChildren(); i++) {
var child = openDoc.getChild(i);
if (child.getType() == DocumentApp.ElementType.LIST_ITEM && child.asText().getText() == template)
child.removeFromParent();
}
}
Output:
Note:
This will not work when the data the bullets are inside the table. That's why I removed the table and have 2 columns in the page instead.
Insert "column break" at the end of the first column so it will not shift when bullets are replaced by a different number of bullets

Replace text with hyperlink using Google apps script

I need to replace the word without affecting hyperlink (hyperlink must be preserved for the replaced word) and those with non hyperlinks the replace must happen in a regular way.
Here is the link of the coded Docs
I have tried with
function run() {
var findtext = "Search";
var replacetext = "Replacer";
var body = DocumentApp.getActiveDocument().getBody();
var foundElement = body.findText(findtext);
while (foundElement != null) {
var foundText = foundElement.getElement().asText();
var startOffset = foundElement.getStartOffset();
var endOffsetInclusive = foundElement.getEndOffsetInclusive();
var hyperlink = foundText.getLinkUrl(0);
foundText.insertText(0, findtext);
foundText.setLinkUrl(startOffset + findtext.length, endOffsetInclusive + findtext.length, hyperlink);
foundText.deleteText(startOffset + findtext.length, endOffsetInclusive + findtext.length)
foundElement = body.findText(findtext, foundElement);
}
}
The main issue is treating the result from findText as a word.
It is tricky because you can't get a "word" element. You have to:
Take the whole paragraph element that findText returns. This contains the search result.
Get the index values of the start and end of the found word.
Get the hyperlink at that index
Delete the text between those indices
Insert the new text and then assign the hyperlink with the new indices.
For example:
foundText.insertText(0, findtext)
Inserts the text you are looking for, i.e. "Search", at the start of the element which the result is in.
This:
var hyperlink = foundText.getLinkUrl(0)
This will only get the hyperlink found at the start of the paragraph, for example, which means that if the first word of the paragraph has a hyperlink, this is what it will return. In getLinkUrl() you should use the start index of the search result.
Solution
This code will replace text and will keep the hyperlink, if it has one.
function replaceTextKeepHyperlink(textToReplace, ReplacementText) {
var body = DocumentApp.getActiveDocument().getBody();
var searchResult = body.findText(textToReplace);
while (searchResult != null) {
// Getting info about result
var foundText = searchResult.getElement().asText();
var start = searchResult.getStartOffset();
var end = searchResult.getEndOffsetInclusive();
var hyperlink = searchResult.getElement().getLinkUrl(start);
// Modifying text
foundText.deleteText(start, end)
foundText.insertText(start, ReplacementText)
foundText.setLinkUrl(start, start + ReplacementText.length - 1, hyperlink)
// Moving to next search result
searchResult = body.findText(textToReplace, searchResult);
}
}
It will not keep any other formatting though, so for that you would have add in some lines to the "Getting info" and "Modifying" parts of the code.
Reference
text methods
Update
mshcruz found that if you called the function with parameters like this:
replaceTextKeepHyperlink("Search", "PrefixedSearch")
The function gets caught in an infinite loop, because it finds the text its looking for in the text its just replaced, replaces that part, and on and on.
He provided the fix which is incorporated below with a try block to avoid the error that it produces if a textToReplace is found at the end of the document:
function replaceTextKeepHyperlink(textToReplace, ReplacementText) {
var body = DocumentApp.getActiveDocument().getBody();
var searchResult = body.findText(textToReplace);
while (searchResult != null) {
var foundText = searchResult.getElement().asText();
var start = searchResult.getStartOffset();
var end = searchResult.getEndOffsetInclusive();
var hyperlink = searchResult.getElement().getLinkUrl(start);
foundText.deleteText(start, end)
foundText.insertText(start, ReplacementText)
foundText.setLinkUrl(start, start + ReplacementText.length - 1, hyperlink)
try {
let rangeBuilder = DocumentApp.getActiveDocument().newRange();
rangeBuilder.addElement(searchResult.getElement(), start, end+ReplacementText.length - 1);
searchResult = rangeBuilder.getRangeElements()[0];
} catch (e){
Logger.log("End of Document")
return null
}
searchResult = body.findText(textToReplace, searchResult);
}
}

Google (Docs) Apps Script - Can't check if cursor on named range

I am inserting text into a document and each text insertion is added to a named range so that I can look them all up with getNamedRanges(NAME) and getNamedRangesById(ID).
Now I need to check if the current cursor position is on a named range and I have yet to figure out how.
This post is similar:
How to determine the named range from position in Google Docs through Google Apps Script
But when the cursor is on a namedrange cursor.getElement() returns Text object, not the named range.
How can I determine if the cursor is currently positioned on a named range?
You want to confirm whether the current cursor position is inside in the namedRange on Google Document.
You want to achieve this using Google Apps Script.
Workaround:
In this workaround, I checked whether the cursor position is included in the namedRange by comparing the indexes of paragraph of both the namedRange and the cursor position.
Flow:
The flow of the script is as follows.
Retrieve the indexes of paragraph of the namedRange.
I this sample script, from your question, the namedRange ID is used.
In this case, there might be multiple paragraphs including table, list and so on. So all indexes in the namedRange are retrieved.
Retrieve the index of paragraph of the cursor position.
Retrieve the index of paragraph of the selected range.
This sample script also checks whether the selected range is in the namedRange. Because when the text is selected, cursor becomes null.
If the cursor or selected range are staying in the namedRange, myFunction() returns true.
If the cursor or selected range are not staying in the namedRange, myFunction() returns false.
Also you can confirm it at the log.
Sample script:
Before you use this script, please set the namedRange ID.
function myFunction() {
var nameRangeId = "###"; // Please set namedRange ID here.
var getIndex = function(doc, e) {
while (e.getParent().getType() != DocumentApp.ElementType.BODY_SECTION) e = e.getParent();
return doc.getBody().getChildIndex(e);
};
var doc = DocumentApp.getActiveDocument();
// For namedRange
var namedRange = doc.getNamedRangeById(nameRangeId);
if (namedRange) {
var indexOfNamedRange = namedRange.getRange().getRangeElements().map(function(e) {return getIndex(doc, e.getElement())});
} else {
throw new Error("No namedRange.");
}
var name = namedRange.getName();
// For cursor
var cursor = doc.getCursor();
if (cursor) {
var indexOfCursor = getIndex(doc, cursor.getElement());
if (~indexOfNamedRange.indexOf(indexOfCursor)) {
Logger.log("Inside of %s", name);
return true;
}
Logger.log("Outside of %s", name);
return false;
}
// For select
var select = doc.getSelection();
if (select) {
var indexOfSelect = select.getRangeElements().map(function(e) {return getIndex(doc, e.getElement())});
if (indexOfSelect.some(function(e) {return ~indexOfNamedRange.indexOf(e)})) {
Logger.log("Inside of %s", name);
return true;
}
Logger.log("Outside of %s", name);
return false;
}
throw new Error("No cursor and select.");
}
Note:
In this script, when the text is selected on Document, the cursor position cannot be retrieved. So I added the function to check the selected range. If you don't want to check the selected range, please remove the script of // For select.
In this script, even only one index of selected range are included in the namedRange, true is returned. About this, please modify for your situation.
In the current stage, this script doesn't suppose about the header and footer sections.
References:
getNamedRangeById()
getCursor()
getSelection()
getChildIndex()
Added:
I had understood that from this situation, OP has set the named range to the paragraph. When I proposed a sample script for this, I thought that I correctly understood OP's goal. But, from gaspar's following comment,
this only shows whether the cursor is in the same element as the named range, but in case of named range partial text it gives a false positive finding if the cursor is in the same element but not in the same text part
If OP sets the part of the paragraph as the named range, and OP wants to check whether the cursor is included in the named range, the sample script is as follows.
Sample script:
function myFunction() {
var nameRangeId = "###"; // Please set namedRange ID here.
var getIndex = function (doc, e) {
while (e.getParent().getType() != DocumentApp.ElementType.BODY_SECTION) e = e.getParent();
return doc.getBody().getChildIndex(e);
};
var doc = DocumentApp.getActiveDocument();
// For namedRange
var namedRange = doc.getNamedRangeById(nameRangeId);
if (namedRange) {
var indexOfNamedRange = namedRange.getRange().getRangeElements().map(e => ({ idx: getIndex(doc, e.getElement()), start: e.getStartOffset(), end: e.getEndOffsetInclusive() }));
} else {
throw new Error("No namedRange.");
}
var name = namedRange.getName();
// For cursor
var cursor = doc.getCursor();
if (cursor) {
var indexOfCursor = getIndex(doc, cursor.getElement());
var offset = cursor.getOffset();
if (indexOfNamedRange.some(({ idx, start, end }) => idx == indexOfCursor && ((start == -1 && end == -1) || (offset > start && offset < end)))) {
Logger.log("Inside of %s", name);
return true;
}
Logger.log("Outside of %s", name);
return false;
}
// For select
var select = doc.getSelection();
if (select) {
var indexOfSelect = select.getRangeElements().map(e => ({ idx: getIndex(doc, e.getElement()), start: e.getStartOffset(), end: e.getEndOffsetInclusive() }));
if (indexOfSelect.some(e => indexOfNamedRange.some(({ idx, start, end }) => idx == e.idx && ((start == -1 && end == -1) || ((e.start > start && e.start < end) || (e.end > start && e.end < end)))))) {
Logger.log("Inside of %s", name);
return true;
}
Logger.log("Outside of %s", name);
return false;
}
throw new Error("No cursor and select.");
}
When I posted my answer, Google Apps Script cannot use V8 runtime. But, now, V8 runtime can be used. So I modified the script using V8 runtime. Please be careful about this.
The solution proposed in the post to which you refer implies looping through your range of interest and checking if one of your range elements equals the element on which the cursor lies.
The code should look like this:
function myFunction() {
var doc = DocumentApp.getActiveDocument();
var cursor = doc.getCursor();
var el=cursor.getElement().asText().getText();
var range;
//specify the name of the range of interest in getNamedRanges()
doc.getNamedRanges('testy').forEach(function(rangeEntry){
(rangeEntry.getRange().getRangeElements().forEach(function(element){
var child=element.getElement().getText();
if(child==el){
Logger.log("Cursor is on named range "+rangeEntry.getName())
}
}))
})
}
Since I also needed this and none of the previous answers work correctly (I tested them all; see my comments), I wrote my own function that actually works, see below.
const cursorIndex = getIndex(cursor.getElement())
let found = false;
let rangeIndex = 0;
// note: to search for any named range, just omit the name
// (hence just give "doc.getNamedRanges()")
// then you can get the name of the found named range via "getName()"
for (const rangeEntry of doc.getNamedRanges('the_named_range_name')) {
for (const element of rangeEntry.getRange().getRangeElements()) {
rangeIndex = getIndex(element.getElement());
if (cursorIndex === rangeIndex) {
if (element.isPartial()) {
let cursorOffset = cursor.getSurroundingTextOffset()
if (cursorOffset >= element.getStartOffset() && cursorOffset <= element.getEndOffsetInclusive() + 1) {
found = true;
break;
}
} else {
found = true;
break;
}
}
}
if (found || rangeIndex > cursorIndex) {
break;
}
}
if (found) {
DocumentApp.getUi().alert("There is a named range here.");
}
(With small modifications, this could also be an answer to this question.)

Having trouble running custom search function on specific cell data for HTML sidebar display

I'm setting up a sidebar in Google Sheets to display information from rows to give my team a better view of certain data. Through a few of Mogsdad's answers (huge props) I was able to set up the sidebar HTML and display functions that update the DOM with info from whichever row is selected. I'm trying to expand this by running a custom search on a specific cell (string) within the row range and adding an element to the DOM that displays the first 5 Google search results; however, I'm having a tough time pointing to the string value in that specific cell (really a column in the array), running it in my custom search function, and getting the function that adds elements to the HTML to append the results.
The inspiration for this use case comes from Grant Timmerman's Apps Script demo at Angular Connect 2018, where he updates the sidebar with info about event speakers and pulls videos from Youtube using a search string from a specific cell. In the below code, you'll see that I pulled a lot from Mogsdad's sheet polling technique and their walkthrough on setting up a custom search engine using Google's API Key protocol.
Here's my .gs setup:
/**
*This function creates the sidebar in Sheets' UI based on HTML I set up *separately.
*/
function checkUpdates() {
var ui = HtmlService.createTemplateFromFile('CheckSidebar')
.evaluate()
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setTitle('Recent Sheet Updates');
SpreadsheetApp.getUi().showSidebar(ui);
};
/**
* Returns the active row.
* All based on Mogsdad's sheet polling function from 2015.
*/
function getRecord() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
var headers = data[0];
var rowNum = sheet.getActiveCell().getRow();
if (rowNum > data.length) return [];
var record = [];
for (var col=0;col<headers.length;col++) {
var cellval = data[rowNum-1][col];
// Here I tried to set another variable to data[rowNum-1][2] to get some //specific data from column #3
Logger.log(cellval);
record.push({ heading: headers[col],cellval: cellval });
}
return record;
}
In a separate editor tab, I have the search function set up, also largely just following Mogsdad's instructions:
function SearchFetch(query) {
var urlTemplate = "https://www.googleapis.com/customsearch/v1?key=%KEY%&cx=%CX%&q=%Q%";
var ApiKey = "custom API Key";
var searchEngineID = "ID for the search engine";
var url = urlTemplate
.replace("%KEY%", encodeURIComponent(ApiKey))
.replace("%CX%", encodeURIComponent(searchEngineID))
.replace("%Q%", encodeURIComponent(query));
var params = {
muteExceptions: true
};
Logger.log(UrlFetchApp.getRequest(url, params));
var response = UrlFetchApp.fetch(url, params);
var respCode = response.getResponseCode();
if (respCode !== 200) {
throw new Error ("Error " +respCode+ " " + response.getContentText());
}
else {
var result = JSON.parse(response.getContentText());
Logger.log("Obtained %s search results in %s seconds.",
result.searchInformation.formattedTotalResults,
result.searchInformation.formattedSearchTime);
return result;
Finally, I have a function between tags in HTML that grabs the values from the array generated by getRecord() and dumps them into a DOM element with the class ID "floatypar" (for floaty-looking paragraphs, naturally). Lots of Mogsdad's markup in here:
function showRecord(record) {
if (record.length) {
for (var i = 0; i < record.length; i++) {
// build field name on the fly, formatted field-1234
var str = '' + i;
var fieldId = 'field-' + ('0000' + str).substring(str.length)
// If this field # doesn't already exist on the page, create it
if (!$('#'+fieldId).length) {
var newField = $($.parseHTML('<div id="'+fieldId+'"></div>'));
$('.floatypar').append(newField);
}
// Replace content of the field div with new record
$('#'+fieldId).replaceWith('<div id="'+fieldId+'" class="floatypar">' +record[i].cellval + '</div>');
}
}
//Setup the next poll
poll();
}
When I run this, the sidebar displays values from each cell in the row in their own div in the sidebar. However, I'm at a loss for passing the values from column #3 to the search engine and displaying results in their own sidebar div. Can anyone steer me in the right direction? Please do give props to Mogsdad in any case!