I'm attempting to make an add-in, whereby a paragraph is split into sentences and then spoken, with the sentences being highlighted as they are read. For the most part, I have this working but the issue comes when I want to highlight the sentences currently being read.
I have a function which splits the paragraph into sentences, based on where the users cursor is:
function selectionTest() {
Word.run(function (context) {
originalRange = context.document.getSelection();
var paragraphs = originalRange.paragraphs;
context.load(paragraphs, 'text');
context.load(originalRange, 'text');
return context.sync().then(function () {
//Range should only be present in a single paragraph, rather than spanning multiple
if (paragraphs.items.length === 1) {
var paragraph = paragraphs.items[0];
var ranges = paragraph.getTextRanges(['.'], true);
context.load(ranges, 'text');
return context.sync(ranges);
}
}).then(function (sentences) {
...
I want to base highlighting the text on the 'originalRange' object, so that the correct sentences are highlighted, like the following:
function highlightSentence(id, colour) {
Word.run(function (context) {
var paragraphs = originalRange.paragraphs;
context.load(paragraphs, 'text');
context.load(originalRange, 'text');
...
But this produces an error as 'originalRange' is being used over multiple contexts. Is there a way for me to use 'originalRange' over multiple contexts or another solution?
UPDATE:
I attempted to get the sentences of the paragraphs again within the function, using 'context.trackedObjects.add' for the original range. This caused the same error when attempting to get the 'paragraphs' property from it.
I realised that all I potentially needed was the sentences of the paragraphs, instead of using the original range to get the sentences again. Instead, I implemented a different solution:
function highlightSentence(id, colour) {
Word.run(function (context) {
context.trackedObjects.add(gSentences);
return context.sync().then(function () {
gSentences.items[id].font.highlightColor = colour;
}).then(context.sync);
}).then(function(){
gSentences.context.trackedObjects.remove(gSentences);
gSentences.context.sync();
}).catch(function(error) {
console.log(error.message);
});
}
However, I now get the following error: "The object path '_reference()' isn't working for what you're trying to do. If you're using the object across multiple \"context.sync\" calls and outside the sequential execution of a \".run\" batch, please use the \"context.trackedObjects.add()\" and \"context.trackedObjects.remove()\" methods to manage the object's lifetime."
UPDATE:
I managed to solve the issue above. However, now, during the highlight function, since the 'gSentences' variable has not been loaded within the context, its properties such as 'font' are not available, so I am unable to change the highlight colour. If I attempt to load it in the context, the original error of 'cannot use objects across contexts' appears. I'm not sure what to do at this point.
UPDATE:
This is what I use to retrieve sentences in a paragraph that are either the same position or after the cursor. These sentences are pushed to an array to be spoken. I found that I had to mess around a lot with callbacks to do this.
function selectionTest() {
Word.run(function (context) {
var range = context.document.getSelection();
var paragraphs = range.paragraphs;
context.load(paragraphs, 'text');
return context.sync().then(function () {
if (paragraphs.items.length === 1) {
var paragraph = paragraphs.items[0];
gSentences = paragraph.getTextRanges(['.'], true);
context.load(gSentences);
return context.sync();
}
}).then(function () {
if (gSentences.items) {
var sentencesResult = '';
var callbacklist = [];
currentSentence = 0;
sentencesToSpeak = [];
function isSentenceinRange(idx, fn) {
var rangeLoc = gSentences.items[idx].compareLocationWith(range);
return context.sync().then(function () {
if (rangeLoc.value === Word.LocationRelation.contains || rangeLoc.value === Word.InsertLocation.after) {
return fn(gSentences.items[idx].text);
}
return fn('');
});
}
for (var i = 0; i < gSentences.items.length; i++) {
callbacklist.push(
(function (i) {
return function () {
isSentenceinRange(i, function (result) {
if (result) {
sentencesToSpeak.push({ id: i, text: result });
if (i === gSentences.items.length - 1) {
sentencesFinialised();
}
}
});
}
})(i)
);
}
for (var callback in callbacklist) {
callbacklist[callback].call(this);
}
}
});
}).catch(function (error) {
console.log(error.message);
});
}
I wanted to highlight the sentences while they were being spoken, which is what the next function would be used to do (called on the onend event listener of audio element)
function highlightSentenceTest(id, colour) {
Word.run(function (context) {
context.trackedObjects.add(gSentences);
//Causes error, but need to load to get access?
context.load(gSentences);
return context.sync().then(function () {
gSentences.items[id].font.highlightColor = colour;
}).then(context.sync)
}).catch(function(error) {
console.log(error.message);
});
}