Method findText() stuck in first paragraph of Google Document - javascript

I'm trying to find certain words in a Google Document, in order to format them via script. The problem is, it seems like findText() is only able to find the word, when it's in the first paragraph. If it's somewhere else, findText() will go nuts and give me (seemingly) random locations for the word. Here is how it's coded:
function findBoldMarks(){
var doc = DocumentApp.openById('docId');
var docText = doc.editAsText();
// search initial bold markup
var srcB0 = body.findText('bold0');
var idB0 = srcB0.getStartOffset();
// search final bold markup
var srcB1 = body.findText('bold1');
var idB1End = srcB1.getEndOffsetInclusive();
// set markup and text wrapped within bold markup as bold
docText.setBold(idB0, idB1End, true);
}
I feel like I must be ignoring something basic here, but after hours struggling with it, I'm not sure anymore. I appreciate any thoughts. Thanks!

To continue searching past the first occurrence, you'll need to wrap the search in loop and provide the second "from" argument in findText().
Here's the start of an example that only searches for 1 word...
function findBoldMarks(){
var body = DocumentApp.getActiveDocument().getBody();
var pattern = "bold0";
var searchResult = body.findText(pattern);
var style = {};
style[DocumentApp.Attribute.BOLD] = true;
while (searchResult !== null) {
var thisElement = searchResult.getElement();
var thisElementText = thisElement.asText();
thisElementText.setAttributes(searchResult.getStartOffset(), searchResult.getEndOffsetInclusive(),style);
// search for next match
searchResult = body.findText(pattern, searchResult);
}
}
...derived from some answers in this post.
Maybe some one can modify to include the text between the two search terms. It doesn't appear that findText() accepts a regular expression search pattern, so another approach may be needed to do that.

Related

I used js to create my command syntax, now how can I use it?

I have a Google Sheet with .gs script that is successfully generating dynamicnewRichTextValue() parameters which are meant to be injected into a Sheet cell that will contain multiple lines of text each with their own URL. I do not know all of the parameters in advance (might be one text and one link, or two each, or more) which is why I am dynamically generating the parameters.
Let's say the end-state should be this (in this case there are only two line items, but there could be more or less:
var RichTextValue=SpreadsheetApp.newRichTextValue()
.setText("mailto:fred#abcdef.com,mailto:jim#abcdef.com")
.setLinkUrl(0,6,"mailto:fred#abcdef.com")
.setLinkUrl(7,19,"mailto:jim#abcdef.com")
.build();
In my script I don't know how many "setText" parameters or "setLinkUrl" statements I will need to generate, so I am doing it dynamically.
This is simple to handle for "setText" because I can just pass a single variable constructed during an earlier loop that builds the "setText" parameters. Let's call that variable setTextContent, and it works like this:
var RichTextValue=SpreadsheetApp.newRichTextValue()
.setText(setTextContent)
So up to this point, everything is great. The problem is that I have another variable that generates the URL portion of the newrichtextvalue() parameters up to the ".build();" statement. So let's call that variable setUrlContent and it is built in an earlier loop and contains the string for the rest of the statement:
.setLinkURL(0,22,"mailto:fred#abcdef.com").setLinkURL(23,44,"mailto:jim#abcdef.com")
I am stumped trying to figure out how to attach it to the earlier bit. I feel like this is something simple I am forgetting. But I can't find it after much research. How do I hook up setUrlContent to the code above so that the command executes? I want to attach the bits above and get back to assigning it all to a variable I can put into a cell:
var emailCell=SpreadsheetApp.newRichTextValue()
.setText("mailto:fred#abcdef.com,mailto:jim#abcdef.com") // I can dynamically create up to here
.setLinkUrl(0,6,"mailto:fred#abcdef.com") // ...but these last couple lines are
.setLinkUrl(7,19,"mailto:jim#abcdef.com") // stuck in a string variable.
.build();
sheet.getRange(1,1,1,1).setRichTextValue(emailCell)
Thanks!
I believe your goal and situation as follows.
You want to use your script by dynamically changing the number of emails.
Modification points:
When your following script is run, I think that the links are reflected to mailto and fred#abcdef..
var emailCell=SpreadsheetApp.newRichTextValue()
.setText("mailto:fred#abcdef.com,mailto:jim#abcdef.com")
.setLinkUrl(0,6,"mailto:fred#abcdef.com")
.setLinkUrl(7,19,"mailto:jim#abcdef.com")
.build();
sheet.getRange(1,1,1,1).setRichTextValue(emailCell)
I thought that you might have wanted the linked email addresses like below.
fred#abcdef.com has the link of mailto:fred#abcdef.com.
jim#abcdef.com has the link of mailto:jim#abcdef.com.
In this answer, I would like to propose the modified script for above direction.
Modified script:
var inputText = "mailto:fred#abcdef.com,mailto:jim#abcdef.com"; // This is your sample text value.
var ar = inputText.split(",").map(e => {
var v = e.trim();
return [v.split(":")[1], v];
});
var text = ar.map(([e]) => e).join(",");
var emailCell = SpreadsheetApp.newRichTextValue().setText(text);
var start = 0;
ar.forEach(([t, u], i) => {
var len = t.length;
emailCell.setLinkUrl(start, start + len, u);
start += len + 1;
});
SpreadsheetApp.getActiveSheet().getRange(1,1,1,1).setRichTextValue(emailCell.build());
In this modification, inputText is splitted to the hyperlink and the text (for example, when your sample value is used, it's fred#abcdef.com and mailto:fred#abcdef.com.), and the text including the hyperlink are put to the cell.
In this case, for example, even when var inputText = "mailto:fred#abcdef.com,mailto:jim#abcdef.com" is modified to var inputText = "mailto:fred#abcdef.com" and var inputText = "mailto:fred#abcdef.com,mailto:jim#abcdef.com,mailto:sample#abcdef.com", each hyperlink are reflected to each text.
Note:
When you want to the hyperlink of mailto:fred#abcdef.com to the text of mailto:fred#abcdef.com, you can also use the following modified script.
var inputText = "mailto:fred#abcdef.com,mailto:jim#abcdef.com"; // This is your sample text value.
var ar = inputText.split(",").map(e => e.trim());
var emailCell = SpreadsheetApp.newRichTextValue().setText(inputText);
var start = 0;
ar.forEach((t, i) => {
var len = t.length;
emailCell.setLinkUrl(start, start + len, t);
start += len + 1;
});
SpreadsheetApp.getActiveSheet().getRange(1,1,1,1).setRichTextValue(emailCell.build());
References:
newRichTextValue()
Class RichTextValueBuilder
Class RichTextValue

Hard Line Break in Google Apps Script (Google Docs with Google Form Field to create new bullet in bulleted list)

I'm using Google Forms to feed a Google Doc (via Google Sheets), and in this Google Doc I have a pre-existing bulleted list. I would like to enter code that will add another bullet to the list.
My approach has been to add a ###newLine### tag to the end of the last pre-filled bullet in the form. I then used replace.Text(###newLine###) in GAS, and then added '\n' for a new line.
The problem is that this '\n' inserts a soft line break (like a Shift+Enter), and it doesn't create a new bullet. It just creates a new line under the prior bullet. I have tested within the doc by adding/removing the bullet associated with the above paragraph, and it is clear that this new line is associated with the above paragraph. What I would like is a a hard line break (like simply pressing Enter) which will create a new bullet.
Here's what the code looks like:
body.replaceText('###newLine###', '\n' + 'Produces a soft (shift+enter) line break.');
Also tried:
body.appendParagraph().
This attached to the end of the body and didn't seem to replaceText.
var insertPar = body.insertParagraph(21, 'test insert paragraph');
body.replaceText('###newBullet###', insertPar);
This would put it in the right spot but not as part of the list.
var listItemTest = body.appendListItem('#listItemTest#');
body.replaceText('###newBullet###', listItemTest);
This appended a numbered list to the end of the body but would not replace text or add to the existing bulleted list.
08-03-19, I tried the following, per Jescanellas's assistance. It works perfectly in the original test doc I provided, but I can't port it over to other docs. I think that's because I'm somehow failing to get the right data to attach it to the list at the right level, but I'm not sure where I'm messing up.
var formDataEntered = functionName.values[11] || ''; //This var is retrieved from the sheet attached to a form. It's the submitted data.
var listItem = body.getListItems(); //We're getting the list.
for (var i = 0; i < listItem.length;i++){ //We're creating a loop here.
var item = body.getListItems()[i]; //This gets the list and applies the loop to it.
if ((item.findText('###bulletTestPlaceholder###')) && (formDataEntered != '')){ //The ###bulletTestPlaceholder### is just a placeholder in the doc where I want to insert the bullet. Your purpose with the item.findText is to identify the list level we're going for - NOT to use the text itself as a placeholder and replace the text.
var index = body.getChildIndex(item); //You're getting all the data about var item (where we got the list and applied the loop).
var level = item.getNestingLevel(); //This gets the nesting level of var item. I'm wondering if this might be the issue as it's not specific to the findText('###bulletTestPlaceholder###')?
var glyph = item.getGlyphType(); //This gets the bullet type.
body.insertListItem((index + 1), formDataEntered).setNestingLevel(level).setGlyphType(glyph); //This is the location in the list where teh bullet will be placed. It also sets the nesting level and glyph type. I've tried playing with the nesting level using integers, but that doesn't fix it.
item.replaceText('###bulletTestPlaceholder###',''); //removes '###bulletTestPlaceholder###' text after it's no longer needed.
break; //stops the loop from looping multiple times.
} else if ((item.findText('###bulletTestPlaceholder###')) && (formDataEntered == '')) {
item.replaceText('###bulletTestPlaceholder###',''); //removes '###bulletTestPlaceholder###' text and avoids new line if no formDataEntered
}
}
After some investigating, and thanks to your examples and links I think I got the solution of this. If there is any issue or I misunderstood something, please tell me and I will correct the post.
This searches for the word ###origApproach### in all the paragraphs (list items) and adds the ###formData### next to it. After this it removes the ###origApproach### paragraph. I commented some lines in case you don't want to remove it.
function myFunction() {
var body = DocumentApp.getActiveDocument().getBody();
var listItem = body.getListItems();
for (var i = 0; i < listItem.length;i++){
var item = body.getListItems()[i];
if (item.findText('###origApproach###')){
var index = body.getChildIndex(item);
var level = item.getNestingLevel();
var glyph = item.getGlyphType();
//In case the indentation is changed:
var indentFirst = item.getIndentFirstLine();
var indentStart = item.getIndentStart();
//Added both setIndents to fix the indentation issue.
body.insertListItem((index + 1), '###formData###').setNestingLevel(level).setGlyphType(glyph).setIndentFirstLine(indentFirst).setIndentStart(indent);
body.removeChild(item); //Comment this if you don't want to remove the ###origApproach### paragraph
//Uncomment this if you want to keep the paragraph and remove ###origApproach###
//item.replaceText('###origApproach###','');
break;
}
}
}
If you remove the paragraph with the word ###origApproach### change index + 1 to index
EDIT
If it's changing your glpyh style, you can force it by using the parameters BULLET, HOLLOW_BULLET, NUMBER, etc instead of the variable glyph.
You can read more about these functions here

More efficient way to append a list item after some text in Google Docs

So at the moment, I have some code that allows me to insert a ListItem after a paragraph in Google Docs. The only issue that I have with it is that it seems overly complicated for what I'm trying to accomplish. Here is my code...
var search = "This is a test"
var body = DocumentApp.getActiveDocument().getBody()
var regExp = new RegExp("("+search+")", "g");
var text;
for (var i = body.getNumChildren() - 1; i >= 0; i--) {
text = body.getChild(i).asText().getText()
if (regExp.test(text)) {
body.insertListItem(i + 1, "This is a test bullet.").setGlyphType(DocumentApp.GlyphType.BULLET).setBold(false)
}
}
Line 1: This is where you put the text into where you want to append the ListItems under
Line 2: This just grabs everything in the body of the document
Line 3: Creates a regular expression with the text that was inputted in line 1
Line 4: Just sets the "text" variable
Line 5: Loops through all the child elements of the body element
Line 6: Gets the text of each of the child elements
Line 7: Test to see if the text in the "search" variable is equal to some text in the body of the document somewhere and grabs that child
Line 8: Inserts a bullet one child beneath the child that is found in line 7
This code below is what I thought would do that for me, append a ListItem below some text
var search = "This is a test"
var body = DocumentApp.getActiveDocument().getBody()
body.findText(search).getElement().asText().appendListItem("Test bullet")
I wanted it to look something like this:
This is a test
Test bullet
But instead with the code directly above I just got this error TypeError: Cannot find function appendListItem in object Text. (line 116, file "Code")
The thing that I am just very confused about is when I run some code that looks like this it works without any error but obviously it appends it to the very top of the document. So then why doesn't my code directly above work and how can I make it work?:
var body = DocumentApp.getActiveDocument().getBody()
body.appendListItem("Test bullet")
Any and all help is greatly appreciated!
Text objects represent text, the lowest level of document structure. One can't have other elements, such as a list item, inside of Text. This is why Text objects don't have appendListItem method; they have appendText method.
Actually, the only parts of a document that can contain ListItem are Body and header/footer/footnote elements: see the document structure. So it is necessary to add ListItem to Body, and this needs to be done with body.insertListItem(index, text) method to have it correctly positioned.
If you only want to add one list item after the first search result, the following will do the job:
var search = "This is a test";
var body = DocumentApp.getActiveDocument().getBody();
var found = body.findText(search);
if (found) {
var elem = found.getElement().getParent();
var index = body.getChildIndex(elem);
body.insertListItem(index+1, "Test bullet");
}
Note that the element you get with found.getElement() is Text, which is not a child of Body: it is going to be inside of something else, likely a Paragraph. Hence the call to getParent to get something that is probably* a child of Body.
If the insertion was meant to be global, run a while loop with findText:
var search = "This is a test";
var body = DocumentApp.getActiveDocument().getBody();
var found = body.findText(search);
while (found) {
var elem = found.getElement().getParent();
var index = body.getChildIndex(elem);
body.insertListItem(index+1, "Test bullet");
found = body.findText(search, found);
}
*Important: this won't work if the search term is buried deeper, say in a Paragraph in a Table or in Equation. Then one needs to call getParent more than once until getting to Body. I didn't try to implement this additional loop, because
it's not clear that you'd want list items in random places like Equations;
it would be more complex than the loop-over-children-of-Body approach that you already have.

Javascript/jQuery - Need to find raw text on the page, capture what follows into variable, and replace original with HTML

So let’s say the text KEYWORDANIMAL:(Cat) appears on a page. I want to search through the page for all instances of KEYWORDANIMAL, and then pull the actual animal, in this case Cat, into a variable to be used in another script that’ll pull in related content. I also want to replace KEYWORDANIMAL:(Cat) with an empty div with concatenated ID to be targeted by the other script (this other script is already working fine by itself).
I've been using info from several other threads here but just cannot make it all come together.
-Find text string using jQuery?
-Get text from character and after using jQuery
-How do I use JQuery to replace all occurring of a certain word in a webpage?
Here's what I have so far:
<p>Here is an animal: KEYWORDANIMAL(Cat)</p>
var findString = $('p:contains("KEYWORDANIMAL")').html();
var startIDString = findString.indexOf('(') + 1;
var endIDString = findString.indexOf(')');
var animalID = findString.substring(startIDString, endIDString);
var embedString1 = "<div id=\"";
var embedString2 = "\"></div>";
var embedStringFull = embedString1 + "animal" + animalID + embedString2;
alert(embedStringFull);
findString.each(function () {
var newDIV = $(this).html().replace('KEYWORDANIMAL', embedStringFull);
$(this).html(newDIV);
});
In fiddle form: http://jsfiddle.net/dC6bj/1/
I got the find part down (probably not very efficiently though), but I am clearly missing something on the replace.
If you absolutely have to do this with JavaScript, you can use a regex replacement function:
var animal_regex = /KEYWORDANIMAL\((.*?)\)/g;
$('p:contains("KEYWORDANIMAL")').each(function() {
var $this = $(this);
var html = $this.html().replace(animal_regex, function(match, name) {
return '<div id="animal' + name + '"></div>';
});
$this.html(html);
});
Demo: http://jsfiddle.net/dnuaL/
This should be done serverside, if possible.
For your third question on how toreplace all occurences of a certain word in a webpage use Regex.Replace like this:
var pagecontent = $('body').html();
var newcontent = Regex.Replace(pagecontent , #"[cat]", "dog");
&('body').html(newcontent);
Regex is the fastest solution for this kind of stuff.
My code example is a bit simple, it would also replace ocurrences within a tag.
or within a word for example in catamaran or .
To make it more perfect you could look for cat preceded by a space and followed by a space or a point or a comma. Read some regex tutorials for this. (It's really worth learning, once you know how to, you'll use it a lot)
Goodluck!

Add link element in div tag with looping

I want to make my DIV element to be able to click and go to the detail page with the ID as a query parameter data to my server.
I have found some examples of possible uses I can use, example :
<div style="cursor:pointer;" onclick="document.location='http://www.google.com'">Foo</div>
It's just that I was confused would like to add the above script into the code that I built.
Part of my code :
for ( var i = 0; i < response.length; ++i ) {
str = response[i].judul;
str2 = response[i].waktu_mulai;
str3 = response[i].channel_code;
var Year,Month,Date,Time,Strip,Join= ""
var Year = str2.substr(0,4)
var Month = str2.substr(5,2)
var Date = str2.substr(8,2)
var Time = str2.substr(-8,8)
var Strip = str2.substr(4,1)
var Join = Date+Strip+Month+Strip+Year+' '+Time
listItem = document.createElement('div');
listItem.setAttribute('data-bb-type', 'item');
listItem.setAttribute('data-bb-img', 'images/icons/logo/'+str3+'.png');
listItem.setAttribute('data-bb-title', str);
listItem.innerHTML = Join+" WIB";
container = document.createElement('div');
container.appendChild(listItem);
bb.imageList.apply([container]);
dataList.appendChild(container.firstChild);
if (bb.scroller) {
bb.scroller.refresh();
}
}
Maybe someone can help me use the link on each DIV additions made ​​by looping my application from database.
Ensure you provide the right context for your question. you're currently posting a snippet of bbUI.js framework script, which might interact completely different as normal HTML, CSS3 and JavaScript.
I also you declare you variables twice. -> var ...... = ""; then again each individual var.
Also try to search StackOverflow first to see if you're asking the same question again. This particular question has already been raised and/or answer many times before and you can find it on many "starting with HTML, Javascript". Though everyone would like to help out, some things are just found in covering the basics and can easily be found by performing the right searches on e.g. Google. People want to see you're putting effort in finding the answer yourself and also see the effort in formulating your question. The better you describe your issue, the more accurate the answer will be.
Back to the subject:
Using just this will solve your problem I think:
<element>.setAttribute('onclick','doSomething();'); // for normal browsers
<element>.onclick = function() {doSomething();}; // for IE
where you can replace 'doSomeThing();' with your own wanted code eg. :
"document.location='http://www.google.com'"
If you want to make it more dynamic, you can also just call a function:
<element>.setAttribute('onclick','myFunction();'); // for normal browsers
<element>.onclick = function() {myFunction();}; // for IE
Where myFunction:
function MyFunction() {
var called_id = this.id;
var call_url = "http://myurl.com/page?id="+called_id;
document.location = call_url;
return; //superflous
}
And as the others remarked try to up your acceptance rate for StackOverFLow, people will be more eager to answer your questions.

Categories

Resources