In my script, I am copying a table of cells that have a lot of text in them. This text has a bunch of custom hyphenation rules that are saved in the document dictionary, NOT in the user dictionary. This is accessed in the UI by opening User dictionary and selecting the document under Target.
When copying the table to another document, these rules are unfortunately not copied with it, and the text is changed.
How can I access this custom document dictionary so that my hyphenations are retained in the target document?
It is possible to access the user dictionary with UserDictionary, but where is the document dictionary located?
Answering this myself since I finally found the proper class to use:
The document dictionary can be accessed using HyphenationExceptions. To get all custom hyphenations from my target document, I did the following:
var myHyphenations = app.activeDocument.hyphenationExceptions;
for (var i = 0; i < myHyphenations.length; i++) {
if (myHyphenations[i].name === "Danish") {
var mySourceDictionary = myHyphenations[i];
mySourceHyphenations = mySourceDictionary.addedExceptions;
break
}
}
For some reason, it seems that it is NOT possible to get a certain HyphenationException using its name.
In other words, the below code does not work (it actually gives me a Norwegian dictionary):
var mySourceDictionary = app.activeDocument.hyphenationExceptions.item("Danish");
For this reason, I had to loop the array until I found the one I needed: ("Danish").
Related
Update: This is a better way of asking the following question.
Is there an Id like attribute for an Element in a Document which I can use to reach that element at a later time. Let's say I inserted a paragraph to a document as follows:
var myParagraph = 'This should be highlighted when user clicks a button';
body.insertParagraph(0, myParagraph);
Then the user inserts another one at the beginning manually (i.e. by typing or pasting). Now the childIndex of my paragraph changes to 1 from 0. I want to reach that paragraph at a later time and highlight it. But because of the insertion, the childIndex is not valid anymore. There is no Id like attribute for Element interface or any type implementing that. CahceService and PropertiesService only accepts String data, so I can't store myParagraphas an Object.
Do you guys have any idea to achieve what I want?
Thanks,
Old version of the same question (Optional Read):
Imagine that user selects a word and presses the highlight button of my add-on. Then she does the same thing for several more words. Then she edits the document in a way that the start end end indexes of those highlighted words change.
At this point she presses the remove highlighting button. My add-on should disable highlighting on all previously selected words. The problem is that I don't want to scan the entire document and find any highlighted text. I just want direct access to those that previously selected.
Is there a way to do that? I tried caching selected elements. But when I get them back from the cache, I get TypeError: Cannot find function insertText in object Text. error. It seems like the type of the object or something changes in between cache.put() and cache.get().
var elements = selection.getSelectedElements();
for (var i = 0; i < elements.length; ++i) {
if (elements[i].isPartial()) {
Logger.log('partial');
var element = elements[i].getElement().asText();
var cache = CacheService.getDocumentCache();
cache.put('element', element);
var startIndex = elements[i].getStartOffset();
var endIndex = elements[i].getEndOffsetInclusive();
}
// ...
}
When I get back the element I get TypeError: Cannot find function insertText in object Text. error.
var cache = CacheService.getDocumentCache();
cache.get('text').insertText(0, ':)');
I hope I can clearly explained what I want to achieve.
One direct way is to add a bookmark, which is not dependent on subsequent document changes. It has a disadvantage: a bookmark is visible for everyone...
More interesting way is to add a named range with a unique name. Sample code is below:
function setNamedParagraph() {
var doc = DocumentApp.getActiveDocument();
// Suppose you want to remember namely the third paragraph (currently)
var par = doc.getBody().getParagraphs()[2];
Logger.log(par.getText());
var rng = doc.newRange().addElement(par);
doc.addNamedRange("My Unique Paragraph", rng);
}
function getParagraphByName() {
var doc = DocumentApp.getActiveDocument();
var rng = doc.getNamedRanges("My Unique Paragraph")[0];
if (rng) {
var par = rng.getRange().getRangeElements()[0].getElement().asParagraph();
Logger.log(par.getText());
} else {
Logger.log("Deleted!");
}
}
The first function "marks" the third paragraph as named range. The second one takes this paragraph by the range name despite subsequent document changes. Really here we need to consider the exception, when our "unique paragraph" was deleted.
Not sure if cache is the best approach. Cache is volatile, so it might happen that the cached value doesn't exist anymore. Probably PropertiesService is a better choice.
I have a JSON response from a server, which returns me a array with 32 objects (in this case). Something like this:
[{object1},{ object2},{ object3}, etc].
Each object have some info that I use to populate an html template. For that, I just use a simple loop:
for(var i = 0; i < api_empresaListar.length; i++)
{
var item = api_empresaListar[i];
var htmls;
htmls = $('...lots of html code');
...
Then it’s just a simple matter of finding/changing the values, and append items on the DOM. Everything works fine. BUT, for some next parts of the code, I would like to access all the info from the object I used to build the html elements (I just show part of the info). So, after searching a lot, I tried to use data, like this:
var tp = htmls.find(".rl_grupo"); // the main div of each html element created in the loop
$(tp).data('key', api_empresaListar[i]); // here, I expected to just insert the object data in each created item.
But when I try it in the console, I got the object info as expected, but always from the last element in the array. Why is that happening? I believe it might be something stupid, but I can’t figure it out.
So, any ideas on how to solve this, or another method to make this work is appreciated. I made it work by setting some "display:none" placeholder html tags and populate those with the info I need later, but looks like a poor solution...
You should not set your htmls variable in the loop. I think that you crush its content every turn, that's why you only have the last item. You should do something like this:
var htmls = $('<div></div>');
for(var i = 0; i < api_empresaListar.length; i++) {
htmls.append($('...lots of html code'));
}
How about setting an index number on each element inside of your html creating code, then iterating over the $('.rl_grupo') elements, like this?
$('.rl_grupo').each(function(){
var index = $(this).data('index');
var currentData = api_empresaListar[index];
$(this).data('key', currentData);
})
Everything I've read says do not save custom properties or attributes to HTML DOM Elements. So I'm trying to figure out how else I should save properties/attributes for an element such that I can access them later.
Originally I was thinking of using the element as the key in a hash but JS converts hash keys to string so that won't work.
Use case:
function do1(element)
{
var w = element.style.width;
element.style.width = "200px";
// i want to save the w variable for this element somewhere/somehow
}
function do2(element)
{
// i want to be able to get the w variable i saved earlier for the element
}
I thought of using the element's ID but the element won't always have an ID that I can use and I can't set one because there might be other JS that dynamically sets IDs for elements.
Why not use data attributes? They're specifically intended for storing extra data on an element.
I'm currently trying to populate a selection list from an external javascript array. It works but I'm trying to populate only certain values using an ID column, which is failing. I'm using check-boxes and an 'If' statement to see which box is checked, and populate the appropriate array values based on this selection. I'm then using another 'If' within a for loop to match the ID value in the array, and add the matching values to the selection. However, it seems that it is completely disregarding the condition and reading the entire array in to selection list. It could be an obvious mistake with my code as I am only a novice.
function populateIslandList () {
var form = document.forms["island_form"];
var islands = form.islands;
if (islands[0].checked){alert("works");
for (i = 0; i < pislands.length; i++)
if (pislands[i][1] = 1){
document.forms["location"].islands.options[i] =
new Option(pislands[i][0], i)}};
if (islands[1].checked){alert("works");
for (i = 0; i < pislands.length; i++)
if (pislands[i][1] = 2){
document.forms["location"].islands.options[i] =
new Option(pislands[i][0], i)}};
}
Your first mistake is here:
var islands = document.getElementById("island_form");
document.getElementById() returns a single DOM element, not a list of objects. So, thus islands[0] and islands[1] are going to be undefined and islands[0].checked will make a script error.
You can only have one DOM element with a given id. You can have multiple elements with a class name so maybe you should switch to using class names and be using document.getElementsByClassName("something")
FYI, you should be looking in the browser error console or debug console to see script errors as this should have given you an indication of some trouble here.
I am trying to obtain the <RowDefinitions> element from my Xaml, through Javascript, so I can add new <RowDefinition> elements to it at runtime.
This way, if a user inputs the number '20', then 20 <RowDefinition> elements will be added to <RowDefinitions>.
The problem is that <RowDefinitions> does not have a possibility for x:Name. It only has x:uid. So would it be possible to fetch the uid from within Javascript? I need the <RowDefinitions> Element one way or another (but only through JS). I need to add <RowDefinition> elements to it.
Any ideas?
Thanks
Assuming Javascript API
There is no such element as <RowDefinitions> you will be refering to the RowDefinitions property of a Grid element which is represented as <Grid.RowDefinitions> in Xaml. Hence you use FindName to aquire the Grid then use GetValue to get the collection of row definitions. Lets assume you have this simple Xaml to start with:-
<Grid
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Loaded="rootGridLoaded" />
So in your Javascript you have can have this code:-
function rootGridLoaded(sender)
{
var plugin = sender.getHost();
var rowDefs = sender.GetValue("RowDefinitions");
for (var i=0; i < 20; i++)
{
var rowDef = plugin.content.createFromXaml("<RowDefinition />");
rowDefs.add(rowDef);
}
}
This will get the RowDefinitions collection from the Grid (which in this case is the sender but you just as easily have used FindName to get a named grid. Then it loops adding 20 RowDefintion instances ot the collection.