Hello and welcome to an Apps Script Crash Course.
I'm Eric Koleda.
And today, we're going to be talking about Cursor and
Selection within Google Docs and how to manipulate and read
it via Apps Script.
We recently published a blog post that goes into the launch
of this feature, as well as some really good samples that
show how it works.
And today, I'm just going to give you a little background
into how Cursor and Selection operates, show you a little
bit of debugging information, and then also we'll code
together some examples that use Cursor and Selection.
So before you can really understand Cursor and
Selection, it's important to understand the structure of a
Google Doc as it exists within Apps Script.
Because you will be using that information a lot when coding
against the Cursor and Selection APIs.
So a great resource for this is within our documentation.
We have a page called Extending Google Docs that
talks about a bunch of different technology you can
use in Apps Script to extend a Google Document.
And there's a whole section on the structure of a document.
And in addition to some description, we have a nice
tree that basically outlines what elements can exist within
a Google Doc and what their hierarchy is like.
So if you've never programmed against our Document App API
before, do read up a little bit on this structure.
And then refer to it as you're coding, as it's very helpful
to kind of understand how different
elements work together.
Another great tool that I'm going to show you a little bit
first is called Cursor Inspector.
So this is a demo that was linked to in the blog post.
And it allows you to kind of explore the raw data that
comes back in the Cursor and Selection APIs without
actually having to do a lot of coding yourself.
So let's see how this sample works.
So you can make a copy of this document from the blog post.
And when you do, you get a menu that says Inspector.
Click Show Sidebar to bring up the Cursor Inspector Sidebar.
And now what this does is it's a page that live loads data
from the Cursor and Selection and shows you just a raw kind
of dump about what you're getting back.
So at the very start of my document is where
my cursor is currently.
And you'll see that my cursor is within a paragraph element.
And it's at offset 0, and the surrounding text is the whole
word of that header, Cursor Inspector.
And [? I'm ?] within 0 within that text.
So let's talk a little bit about what offsets mean.
When you're in the element type other than text--
so something like a paragraph, a table cell, a list item--
those are what we call container elements.
And they're elements that themselves are kind of buckets
that hold things.
They can hold text.
They can hold inline images, drawings, a variety of
different types of content.
So when your cursor is being reported as within one of
those types, like paragraph here, the offset means which
element within the bucket is your cursor in front of.
So right now, my cursor is in front of the 0-th, or first
element, within the paragraph.
In this case, there's only one element within the paragraph,
and it's a text.
Now, if I were to move my cursor one to the right in
between the C and the U, you'll see that the element
type switches to text.
And the offset is now 1.
So now the meaning changes slightly.
When you're within a text, the offset means what character
index within the string of the text is your
cursor in front of.
So in this case, now it's in front of the first or
character index 1, the U, within
the text Cursor Inspector.
And when I get to the end of the text, you'll see that it
once again switches back to the paragraph.
And now the offset is 1, meaning I'm at the end of all
of the things within my paragraph.
Within the outer paragraph, I am right before the child at
index 1, which doesn't exist yet, which means it's very
easy to add a new child to this paragraph.
So you can use this Cursor Inspector yourself to play
around and see how Cursor and Selection change.
And let's see what happens with Selection now.
So Selection--
I can very simply start by just selecting some text, like
let's say this U-R-S. You'll see what I get is
one selected element.
It's a partial selection because I only have part of
that text element selected.
And then it gives me the indices within it that have
been selected.
You'll see I have no cursor when a selection is active.
At any time, you have either a cursor or a
selection but not both.
Now, if I were to select this entire paragraph here all the
way from start to end, you'll see that the element in my
selection changes from a text to a paragraph.
And it's not partial.
I'm selecting the entire paragraph.
And the start and end are negative 1, which indicate no
valid data.
Don't use them.
But the thing about selections, unlike a cursor,
is that they can contain many elements at once.
So I can for instance, select all of these list items.
And you'll see that they all appear
here within my selection.
And so when you're working with Selection, be aware that
there could be many things selected at once.
All right, so now that we understand the basis of Cursor
and Selection, let's actually do a little coding.
So I've created a script attached to this doc.
So I'll go to Tools, Script Editor and select my script,
Cursor Fun.
Because we're about to have some cursor fun.
So let's create a new function that does insertHelloWorld.
This is what we're going to do first, just kind of
demonstrate how you can use Cursor API to insert some text
wherever the cursor is located.
This is useful when you want to, let's say, import some
information into a document wherever the
user has their cursor.
So the first thing I need to do is get ahold of the cursor.
So the cursor is within the document.
And so we'll use DocumentApp.
We'll get the active document, which is the one we
currently have open.
And then we'll get the cursor.
Now, like I said, a cursor may not always exist.
For instance, if there's a selection, there's
going to be no cursor.
So we do want to test first if there actually is a cursor
available for us to work with.
And if there is, it's very easy for us to insert some
text at that cursor using insertText.
So I'll insert Hello World.
And that's it.
We're done.
So let's position my cursor somewhere.
Looks like I had an old Hello World here
from a previous run.
Let's delete that.
So our cursor is right here in that first paragraph.
And we'll run insertHelloWorld.
Now, I already authorized this script, but if you haven't,
you might need to authorize it.
Just a simple clicking of Accept.
We go back to the document.
And we'll see there's Hello World inserted in the document
right where the cursor was.
Like I said, now, obviously I could have just pasted the
word Hello World.
But when you are developing a script, it could be very
useful to paste all sorts of
automatically generated content.
So that's working with Cursor.
Now let's code up a little bit of Selection.
So let's say I wanted to take all the selected text within a
selection and bold it.
Right?
So you can only bold text.
You can't bold images.
So we need to add a little bit of logic that removes things
that can't be bolded.
And then we also need to add a little logic that handles
partial selections.
So if I select part of a text element, I don't want to bold
the entire thing.
I just want to bold the part that has been selected.
So let's write that function.
So I'll create a new function here called boldMe.
And much like before, I need to get ahold of
the selection object.
So in this case, it's still within the active document.
Typos galore.
And within there is getSelection.
And once again, we do want to test to see if there is a
selection, because there might not always be.
There's a cursor.
And now what we need to do is pull out a list of all of the
selected elements, so all of the elements
that have been selected.
Basically, what those rows in the table were before.
So I'll do var Selected Elements is selection dot
getSelectedElements.
Now, we want to loop over these, so we'll do
a simple For loop.
Great.
And now we need to pull out the element that is within
that selection.
So a selected element is kind of a
wrapper around an element.
And so you can see a selected element has the element
itself, whether or not it's been partially selected, and
then the start and end offset within that if it
is partially selected.
So I'm going to pull out the actual element that has been
selected and do a test on it.
So we need to know, can this element be treated as text?
That means, is it a paragraph, is a text, is it a list item?
If it can be treated as text, then we can bold it.
Otherwise, we can't.
So what I can do is do a test where If the element dot
editAsText.
So it doesn't autocomplete because it's not in the outer
element class.
But basically, if the element that we have ahold of right
now has this method available, then we know we
can treat it as text.
And if this method isn't available,
then we know we can't.
So now we know it can be edited as text, we can
actually grab the text.
So I'll do element dot editAsText.
So there's our text.
Now we're ready to bold this text, but we need to know, do
we bold the whole thing or just part of it?
And so for that, we once again need to do a
test on that isPartial.
So if the selected element is only partially selected, we'll
do one thing.
Otherwise, we'll do another.
Now, if it's not partially selected, it's
actually quite easy.
We can just do text dot setBold.
And we're done.
Now, if it's partially selected, we have to only bold
some of it.
So what we can do here is setBold.
And you can refer to the documentation as to the
parameters.
But the parameters are going to be the start, the end, and
whether or not to make it bold.
And actually this takes a True to make it bold, as well.
So the start is going to be the getStartOffset.
The end is going to be getEndOffset.
And then we want to make it bold.
So we'll put the word True here.
So just to overview, we grabbed the selection, we made
sure it was there, we got all the elements.
For all the elements that can be treated as text, we got
them as text.
And then if it's partial, we only bolded part of it.
And if it's not, we bolded the whole thing.
So let me save my code and run the boldMe function.
So let me select some text here.
So what I'm going to do is I'm going to select all of the
first Kittens, the two pictures of kittens, as well
as part of the last word of Kittens.
Now, if we turn back on Cursor and Selector, you'll see we
have some text, some images, and some more text.
If we run the boldMe function, and I go back to my document,
you'll see that the whole first word Kittens got bolded.
The images were left alone-- they can't be bolded.
And then I only bolded the second word of Kittens up
until the selection.
So this is a little bit of an overview about how Cursor and
Selection works.
But there's a lot more that you can try
out and learn yourself.
Like I mentioned, we did publish a blog post.
And the URL is going to be shown below.
Go check it out.
We have some more information on how to use Cursor and
Selection, some great samples to try out, and links to our
reference documentation that give you kind of all the
detail you need to know about how to
use Cursor and Selection.
Thank you for joining me, and have a good one.