I need to insert some line breaks before certain text in a Google Document.
Tried this approach but get errors:
var body = DocumentApp.getActiveDocument().getBody();
var pattern = "WORD 1";
var found = body.findText(pattern);
var parent = found.getElement().getParent();
var index = body.getChildIndex(parent);
// or parent.getChildIndex(parent);
body.insertParagraph(index, "");
Any idea on how to do this?
Appreciate the help!
For example, as a simple modification, how about modifying the script of https://stackoverflow.com/a/65745933 in your previous question?
In this case, InsertTextRequest is used instead of InsertPageBreakRequest.
Modified script:
Please copy and paste the following script to the script editor of Google Document, and please set searchPattern. And, please enable Google Docs API at Advanced Google services.
function myFunction() {
const searchText = "WORD 1"; // Please set text. This script inserts the pagebreak before this text.
// 1. Retrieve all contents from Google Document using the method of "documents.get" in Docs API.
const docId = DocumentApp.getActiveDocument().getId();
const res = Docs.Documents.get(docId);
// 2. Create the request body for using the method of "documents.batchUpdate" in Docs API.
let offset = 0;
const requests = res.body.content.reduce((ar, e) => {
if (e.paragraph) {
e.paragraph.elements.forEach(f => {
if (f.textRun) {
const re = new RegExp(searchText, "g");
let p = null;
while (p = re.exec(f.textRun.content)) {
ar.push({insertText: {location: {index: p.index + offset},text: "\n"}});
}
}
})
}
offset = e.endIndex;
return ar;
}, []).reverse();
// 3. Request the request body to the method of "documents.batchUpdate" in Docs API.
Docs.Documents.batchUpdate({requests: requests}, docId);
}
Result:
When above script is used, the following result is obtained.
From:
To:
Note:
When you don't want to directly use Advanced Google services like your previous question, please modify the 2nd script of https://stackoverflow.com/a/65745933 is as follows.
From
ar.push({insertPageBreak: {location: {index: p.index + offset}}});
To
ar.push({insertText: {location: {index: p.index + offset},text: "\n"}});
References:
Method: documents.get
Method: documents.batchUpdate
InsertTextRequest
Related
I am trying to figure out how to make a script to run a function based on an answer provided, at the moment my script creates and populates a word doc with answers provided from a google form. I want to streamline it so that I can use one google form to create different documents based on an answer provided rather than creating multiple google forms. I am very new to JavaScript and I am pretty sure I need an if statement at the beginning of everything, but I don't know what I should write or where it should go. This is my script:
function onOpen() {
const ui = SpreadsheetApp.getUi();
const menu = ui.createMenu('AutoFill Docs');
menu.addItem('Create New Docs', 'createNewGoogleDocs');
menu.addToUi();
}
function createNewGoogleDocs(){
const googleDocTemplate = DriveApp.getFileById('google file goes here');
const destinationFolder = DriveApp.getFolderById('google folder goes here');
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Response');
const rows = sheet.getDataRange().getValues();
rows.forEach(function(row,index) {
if (index === 0) return;
if (row[9]) return;
const copy = googleDocTemplate.makeCopy(`${row[5]} document name goes here`, destinationFolder);
const doc = DocumentApp.openById(copy.getId())
const body = doc.getBody();
body.replaceText('{{Name}}', row[5]);
body.replaceText('{{To}}', row[6]);
body.replaceText('{{Items}}', row[7]);
body.replaceText('{{Reasoning}}', row[8])
body.replaceText('{{Submitter}}', row[1]);
body.replaceText('{{Role}}', row[2]);
body.replaceText('{{Time}}', row[3]);
body.replaceText('{{Discord}}', row[4]);
doc.saveAndClose();
const url = doc.getUrl();
sheet.getRange(index + 1, 10).setValue(url)
})
}
I have tried innerHTML instead of getBody() but still get an error, and any help with debugging on apps-script, mine does not seem to work.
function findText(findme,colour,desc,rule_name) {
var body = DocumentApp.getActiveDocument().getBody();
var regExp = case_insensitive(findme);
var foundElement = body.findText(regExp);
while (foundElement != null) {
var foundText = foundElement.getElement().asText();
var start = foundElement.getStartOffset();
var end = foundElement.getEndOffsetInclusive();
foundText.setBackgroundColor(start, end, colour);
number_oresults++;
foundElement = body.findText(regExp, foundElement);
var pusher = '<p><span style="background-color:'+colour+'"><b>'+rule_name+'</b> - '+ desc +'</span></p>';
results.push(pusher);
}
}
Looks like you're running a standalone script, but DocumentApp.getActiveDocument() is only available in container-bound scripts.
You can copy-paste your script into a new script bound to that Google Doc or use one of the other open methods:
DocumentApp.openById()
DocumentApp.openByUrl()
Sometimes the active methods when used in method chainging can throw errors like the one shown in your screenshot (`can't read property something of null) See Why Class Range getValues sometimes returns [[]] when chained to Class Sheet getActiveRange?
Try replacing
var body = DocumentApp.getActiveDocument().getBody();
by
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
It's worthy to note that DocumentApp.getActiveDocument() can only be used on bounded scripts and on standalone scripts when be executed as add-on either by using Run > Test as add-on... or by publishing the script as a G Suite Editor add-on for a Google Documents and executing the script from the UI.
NOTE: The following works fine on a standalone script executed Run > Test as add-on...
function onOpen(e) {
DocumentApp.getUi()
.createAddonMenu()
.addItem('Test 1', 'doSomething1')
.addItem('Test 2', 'doSomething2')
.addToUi()
}
function doSomething1(){
var body = DocumentApp.getActiveDocument().getBody();
body.appendParagraph('Test 1');
}
function doSomething2(){
var doc = DocumentApp.getActiveDocument();
var body = doc.getBody();
body.appendParagraph('Test 2');
}
I want to parse some html with htmlparser2 module for Node.js. My task is to find a precise element by its ID and extract its text content.
I have read the documentation (quite limited) and I know how to setup my parser with the onopentag function but it only gives access to the tag name and its attributes (I cannot see the text). The ontext function extracts all text nodes from the given html string, but ignores all markup.
So here's my code.
const htmlparser = require("htmlparser2");
const file = '<h1 id="heading1">Some heading</h1><p>Foobar</p>';
const parser = new htmlparser.Parser({
onopentag: function(name, attribs){
if (attribs.id === "heading1"){
console.log(/*how to extract text so I can get "Some heading" here*/);
}
},
ontext: function(text){
console.log(text); // Some heading \n Foobar
}
});
parser.parseComplete(file);
I expect the output of the function call to be 'Some heading'. I believe that there is some obvious solution but somehow it misses my mind.
Thank you.
You can do it like this using the library you asked about:
const htmlparser = require('htmlparser2');
const domUtils = require('domutils');
const file = '<h1 id="heading1">Some heading</h1><p>Foobar</p>';
var handler = new htmlparser.DomHandler(function(error, dom) {
if (error) {
console.log('Parsing had an error');
return;
} else {
const item = domUtils.findOne(element => {
const matches = element.attribs.id === 'heading1';
return matches;
}, dom);
if (item) {
console.log(item.children[0].data);
}
}
});
var parser = new htmlparser.Parser(handler);
parser.write(file);
parser.end();
The output you will get is "Some Heading". However, you will, in my opinion, find it easier to just use a querying library that is meant for it. You of course, don't need to do this, but you can note how much simpler the following code is: How do I get an element name in cheerio with node.js
Cheerio OR a querySelector API such as https://www.npmjs.com/package/node-html-parser if you prefer the native query selectors is much more lean.
You can compare that code to something more lean, such as the node-html-parser which supports simply querying:
const { parse } = require('node-html-parser');
const file = '<h1 id="heading1">Some heading</h1><p>Foobar</p>';
const root = parse(file);
const text = root.querySelector('#heading1').text;
console.log(text);
I am learning Javascript. I am working on reading RSS feeds for a personal project. I am using 'RSS-parser' npm library to avoid CORS error.
And also I am using Browserify bundler to make it work on the browser.
When I run this code on the terminal it gives me output without any issue. But when I try with the browser it prints nothing.
My knowledge about Asynchronous JS is limited but I am pretty sure it doesn't have errors in here as I added code to it without changing existing code.
let Parser = require('rss-parser');
let parser = new Parser();
let feed;
async () => {
feed = await parser.parseURL('https://www.reddit.com/.rss');
feedTheList();
};
// setTimeout(function() {
// //your code to be executed after 1 second
// feedTheList();
// }, 5000);
function feedTheList()
{
document.body.innerHTML = "<h1>Total Feeds: " + feed.items.length + "</h1>";
let u_list = document.getElementById("list")[0];
feed.items.forEach(item => {
var listItem = document.createElement("li");
//Add the item text
var newText = document.createTextNode(item.title);
listItem.appendChild(newText);
listItem.innerHTML =item.title;
//Add listItem to the listElement
u_list.appendChild(listItem);
});
}
Here is my HTML code.
<body>
<ul id="list"></ul>
<script src="bundle.js"></script>
</body>
Any guidance is much appreciated.
document.getElementById() returns a single element, not a collection, so you don't need to index it. So this:
let u_list = document.getElementById("list")[0];
sets u_list to `undefined, and you should be getting errors later in the code. It should just be:
let u_list = document.getElementById("list");
Also, when you do:
listItem.innerHTML =item.title;
it will replace the text node that you appended on the previous line with this HTML. Either append the text node or assign to innerHTML (or more correctly, innerText), you don't need to do both.
Looks like the async call is not being executed; You need to wrap it
in an anonymous function call:
See the example here:
https://www.npmjs.com/package/rss-parser
Essentially,
var feed; // change let to var, so feed can be used inside the function
// wrap the below into a function call
(async () => {
feed = await parser.parseURL('https://www.reddit.com/.rss');
feedTheList();
})(); // the (); at the end executes the promise
Now it will execute and feed should have items.
CORS errors when making request
As noted in the documentation at https://www.npmjs.com/package/rss-parser, if you get CORS error on a resource, use a CORS proxy. I've updated their example to fit your code:
// Note: some RSS feeds can't be loaded in the browser due to CORS security.
// To get around this, you can use a proxy.
const CORS_PROXY = "https://cors-anywhere.herokuapp.com/"
let parser = new RSSParser();
(async () => {
await parser.parseURL(CORS_PROXY + 'https://www.reddit.com/.rss', function(err, feed) {
feedTheList(feed);
});
})();
function feedTheList(feed)
{
// unchanged
}
One last thing:
The line
document.body.innerHTML = "<h1>Total Feeds: " + feed.items.length + "</h1>";
Will remove all of the content of <body>
I suggest to look into how element.appendChild works, or just place the <h1> tag in your HTML and modify its innerHTML property instead.
I'm trying to write a little script to make my coworkers and mine lives easier. I am trying to append lines to a spreadsheet based on information entered into a custom form. The code posted below just the doPost block which should be appending the google spreadsheet.
function doPost(form) {
var PN = form.PartNumber;
var REV = form.Revision;
var DATE = form.RevisionDate;
var DESC = form.Description;
var NOTE = form.PartNotes;
var URL = form.myFile.getURL();
var ss = SpreadsheetApp.openById("ID HERE"); // removed ID for sake of safety (let me be paranoid)
var sheet = ss.getSheetName('Uploads');
sheet.appendRow([PN,REV,DATE,DESC,NOTE,URL]);
}
I am unsure why it isn't writing to the spreadsheet but it isn't throwing me any errors. If you can offer any insight as to what is wrong I would greatly appreciate it; there are many guides online but most seem to be based on deprecated functions/code/etc.
Thanks for your time.
Instead of using doPost, set up a "On form submit" trigger.
You need to get the namedValues to be able to pull specific values and take the first output.
Also, it should be "getSheetByName('Uploads')" .
As pointed out in the previous answer, it is unclear what you are trying to achieve by "form.myFile.getURL();" If you want to get the form url you might as well create it as a string, as it always stays the same.
Here is a working example of your code:
function doPost(form) {
var formResponses = form.namedValues;
var PN = formResponses.PartNumber[0];
var REV = formResponses.Revision[0];
var DATE = formResponses.RevisionDate[0];
var DESC = formResponses.Description[0];
var NOTE = formResponses.PartNotes[0];
//var URL = form.myFile.getURL(); //Not sure what you are tyring to get here as form URL will always be the same.
var URL = "Your form's url"; //You can put the form url in here so it will be pushed in to every row.
var ss = SpreadsheetApp.openById("ID HERE"); // removed ID for sake of safety (let me be paranoid)
var sheet = ss.getSheetByName('Uploads');
sheet.appendRow([PN,REV,DATE,DESC,NOTE,URL]);
}
The form fields are nested in a "parameter" property in the doPost parameter.
So, you should access them using:
function doPost(form) {
var actualForm = form.parameter;
var PN = actualForm.PartNumber;
//etc
To double check all parameters your receiving and their names, you could append to your sheet everything stringfied, like this:
sheet.appendRow([JSON.stringify(form)]);
--edit
This form.myFile.getURL() also looks odd. I guess another good debugging trick you could do is to wrap everything in a try-catch and email yourself any errors you get. For example:
function doPost(form) {
try {
//all your code
} catch(err) {
MailApp.sendMail('yourself#etc', 'doPost error', err+'\n\n'+JSON.stringify(form));
}
}
On form submit
onFormSubmit works. "doPost" looks wrong.
Simple example:
function Initialize() {
var triggers = ScriptApp.getProjectTriggers();
for(var i in triggers) {
ScriptApp.deleteTrigger(triggers[i]);
}
ScriptApp.newTrigger("SendGoogleForm")
.forSpreadsheet(SpreadsheetApp.getActiveSpreadsheet())
.onFormSubmit()
.create();
}
function SendGoogleForm(e)
{
try
{
Full example - Scroll down to the code http://www.labnol.org/internet/google-docs-email-form/20884/ (Note: example sends email)
Trigger docs: https://developers.google.com/apps-script/guides/triggers/events
Notes: I think the problem is doPost, Does it work with google Forms? Never seen it used with google forms.
First and foremost, thank you everyone who has responded with information thus far. None of the solutions posted here worked for my particular implementation (my implementation is probably to blame, it is very crude), but they definitely set me down the path to a working version of my form which we now lightly use. I have posted some of the code below:
function sheetFill(form, link) {
try {
var formResponses = form.namedValues;
var toForm = [0,0,0,0,0,0,0];
for (i=0;i < form.PartNumber.length;i++){
toForm[0] = toForm[0]+form.PartNumber[i];
}
... (several for loops later)
var d = new Date();
var ss = SpreadsheetApp.openById("IDHERE");
var sheet = ss.getCurrentSheet;
ss.appendRow([toForm[0], toForm[1], toForm[2], toForm[3], toForm[4], toForm[5], toForm[6], link, d]);
} catch(err) {
MailApp.sendEmail('EMAIL', 'doPost error', err+'\n\n'+JSON.stringify(form));
}
}
It is not very versatile or robust and isn't elegant, but it is a starting point.