Word JS APIs: extending a Range - ms-word

While working on answering this question I would really like to have been able to extend a Range by a specific number of characters. In the COM API I would have used Range.MoveEnd(). Is there any equivalent that I didn't find in the JS API?
Background: The question referenced is about finding search terms with more than 255 characters - which is a limit in Word for the desktop. The search fails.
The simple way to go about it would be to search the first 254 characters, then expand the found Range by the remaining number of characters and comparing that Range.Text to the full search term.
Not finding any equivalent for expanding a Range in this manner, I had to resort to:
breaking down the search term into < 255 character pieces
search for each piece one-by-one
determine whether each searched piece was adjacent to the previous
then expand a Range to include the adjacent piece
and repeat until all pieces were found
Thus, my question...
async function basicSearch() {
await Word.run(async (context) => {
let maxNrChars = 254;
let searchterm = "";
let shortSearch = true; //search string < 255 chars
let fullSearchterm = "Video provides a powerful way to help you prove your point. When you click Online Video, you can paste in the embed code for the video you want to add. You can also type a keyword to search online for the video that best fits your document. Aösdlkvaösd faoweifu aösdlkcj aösdofi "
let searchTermNrChars = fullSearchterm.length;
let nrSearchCycles = Number((searchTermNrChars / maxNrChars).toFixed(0));
let nrRemainingChars = searchTermNrChars - (nrSearchCycles * maxNrChars);
//console.log("Number of characters in search term: " + searchTermNrChars
// + "\nnumber of search cycles required: " + nrSearchCycles
// + "\nremaining number of characters: " + nrRemainingChars);
//numerous ranges are required to extend original found range
let bodyRange = context.document.body.getRange();
bodyRange.load('End');
let completeRange = null;
let resultRange = null;
let extendedRange = null;
let followupRange = null;
let cycleCounter = 0;
let resultText = "";
if (searchTermNrChars > maxNrChars) {
searchterm = fullSearchterm.substring(0, maxNrChars);
cycleCounter++;
shortSearch = false;
}
else { searchterm = fullSearchterm; }
let results = context.document.body.search(searchterm);
results.load({ select: 'font/highlightColor, text' });
await context.sync();
// short search term, highlight...
if (shortSearch) {
for (let i = 0; i < results.items.length; i++) {
results.items[i].font.highlightColor = "yellow";
}
}
else {
//console.log("Long search");
for (let i = 0; i < results.items.length; i++) {
resultRange = results.items[i];
resultRange.load('End');
extendedRange = resultRange.getRange('End').expandTo(bodyRange.getRange('End'));
await context.sync();
//search for the remainder of the long search term
for (let cycle = 1; cycle < nrSearchCycles; cycle++) {
searchterm = fullSearchterm.substring((cycle * maxNrChars), maxNrChars);
//console.log(searchterm + " in cycle " + cycle);
let CycleResults = extendedRange.search(searchterm);
CycleResults.load({ select: 'text, Start, End' });
await context.sync();
followupRange = CycleResults.items[0];
//directly adjacent?
let isAfter = followupRange.compareLocationWith(resultRange);
if (isAfter.value == Word.LocationRelation.adjacentAfter) {
resultRange.expandTo(followupRange);
extendedRange = resultRange.getRange('End').expandTo(bodyRange.getRange('End'));
}
await context.sync();
}
if (nrRemainingChars > 0) {
console.log("In remaining chars");
searchterm = fullSearchterm.substring(searchTermNrChars - nrRemainingChars);
console.log(searchterm);
let xresults = extendedRange.search(searchterm);
xresults.load('end, text');
await context.sync();
let xresult = xresults.items[0];
let isAfter = xresult.compareLocationWith(resultRange);
await context.sync();
console.log(isAfter.value);
if (isAfter.value == Word.LocationRelation.adjacentAfter) {
completeRange = resultRange.expandTo(xresult);
completeRange.load('text');
//completeRange.select();
await context.sync();
resultText = completeRange.text.substring(0, fullSearchterm.length);
console.log("Result" + cycleCounter + ": " + resultText);
}
}
else {
//No remeaining chars
resultRange.load('text');
//resultRange.select();
await context.sync();
resultText = resultRange.text.substring(0, fullSearchterm.length);
completeRange = resultRange;
}
//long search successful?
if (resultText == fullSearchterm) {
completeRange.font.highlightColor = "yellow";
}
else {
console.log("Else! " + resultText + " / " + fullSearchterm);
}
completeRange = null;
}
}
});

That was something we had in the original design, but it was actually removed from the API as it can easily lead to unexpected outcomes (i.e. hidden character inconsistencies, footnotes, etc.), and we could not cover those cases with the resources at hand. We decided to remove it.
That been said I think you can achieve something similar to range.MoveEnd() with Word.js, you just need to define to the end of what ;). One way of doing it is to use the range.expandTo(endRange) method. Again, The interesting thing is how to get the "endRange", so the following example shows how to do it if "end" means the end of the paragraph, probably in your scenario will suffice.
async function run() {
await Word.run(async (context) => {
//assume the range at the end of your 255 characters.
var startRange = context.document.getSelection().getRange("end");
//This one is the range at the end of the paragraph including the selection.
var endRange = context.document.getSelection().paragraphs.getLast().getRange("end");
var deltaRange = startRange.expandTo(endRange);
context.load(deltaRange);
await context.sync();
// this will print the characters after the range all the way to the end of the paragraph.
console.log(deltaRange.text);
});
}
hope this helps or at least sets you up in the right direction.

Related

Main thread messes up sorting

Dispatching the queue messes up the order in the array as noted below. I'm trying to rank the array and then be able to translate it. So far its not working:
let top5 = Array(labels.sorted{ $0.confidence > $1.confidence}.prefix(upTo:5))
for lulu in top5 {
let translator = ROGoogleTranslate()
var params = ROGoogleTranslateParams()
params.source = "en"
params.target = "es"
params.text = "\(String(describing: lulu.label))"
translator.translate(params: params, callback: { (result) in
DispatchQueue.main.async {
self.textUno.text = self.textUno.text! + "\(lulu.label)" + " \(lulu.confidence*100)\n"
self.textDos.text = self.textDos.text! + "\(result)\n"
self.view.addSubview(self.textUno)
self.view.addSubview(self.textDos)
}
})
}
If I try to put the sorting out of DispatchQueue.main.async then the translation won't be lined up with the right word.
How can I fix this so that the array is sorted and the translation matches up ?
Translate the array first before ranking them.
Make it simpler first and make sure it is working and then put all the parts together.
If you really want to do it this way you will need to put them into a temporary array after sorting them and then use that at the end.
This, as you said, will return a jumbled result.
The below example is pretty close, needs a bit of a polish but you should be able to do it from this.
let top5 = Array(labels.sorted{ $0.confidence > $1.confidence}.prefix(upTo:5))
var tmparr : []
var count: Int = 0
for lulu in top5 {
let translator = ROGoogleTranslate()
count = count + 1
var params = ROGoogleTranslateParams()
params.source = "en"
params.target = "es"
params.text = "\(String(describing: lulu.label))"
params.ordernumber = count
translator.translate(params: params, callback: { (result) in
tmparr.append[params]
})
}
DispatchQueue.main.async {
for lulunew in tmparr {
if (lulunew.ordernumber == correctindex){
self.textUno.text = self.textUno.text! + "\(lulu.label)" + " \(lulu.confidence*100)\n"
self.textDos.text = self.textDos.text! + "\(result)\n"
self.view.addSubview(self.textUno)
self.view.addSubview(self.textDos)
}
}
}

Swift: Cannot assign value of type 'String.CharacterView.index' to type 'Int'

Does anyone know why I get the following error when I run my code below?
Error: Cannot assign value of type 'String.CharacterView.index' to type 'Int'
var problem = "find the longest word in the problem description"
var last = problem.characters.last
let lastIndex = problem.characters.endIndex;
print(last);
var words:[String] = [String]();
var word:String = "";
var lastCheck:Int = 0;
for i in problem.characters{
lastCheck = lastCheck + 1
if i != " "{
word = word + String(i)
}
else if lastCheck = lastIndex{
words.append(word);
}
else if i == " "{
words.append(word)
word = ""
}
}
print(words)
UPDATE
I have tried changing the let lastIndex to var lastIndex but no luck
You can't compare lastCheck (an Int) and lastIndex (an Index).
You have to convert it:
var problem = "find the longest word in the problem description"
var last = problem.characters.last
let lastIndex = problem.characters.endIndex;
print(last);
var words:[String] = [String]();
var word:String = "";
var lastCheck:Int = 0;
for i in problem.characters{
lastCheck = lastCheck + 1
let lastIndexInt = problem.characters.startIndex.distanceTo(end: lastIndex) // new
if i != " "{
word = word + String(i)
}
else if lastCheck = lastIndex{
words.append(word);
}
else if i == " "{
words.append(word)
word = ""
}
}
print(words)
Here's a simpler solution to your problem:
let input = "find the longest word in the problem description";
let longest = input
.characters //get the input characters
.split(separator:" ", //split by spaces
maxSplits: 1000, //max number of splits
omittingEmptySubsequences: true) //omit "" splits
.map(String.init) //make new strings from the characters
.max{$0.characters.count < $1.characters.count} //get longest length word
print(longest)
Thanks to originaluser2
String.CharacterView.Index is a struct but not Int. So you cannot assign a Int to your String.CharacterView.Index variable. It's normal.
You have to convert to Int as #Amomchilov's answer, or use Index :
...
var lastCheck:String.CharacterView.Index = problem.startIndex
for i in problem.characters {
lastCheck = lastCheck.advancedBy(1)
...
}
Anyway, in order to find the longest word of a phrase, you can use builtIn function of Swift to get all words then compare their length.
For example:
let str = "find the longest word in the problem description"
var longestWord : String?
str.enumerateSubstringsInRange(str.startIndex..<str.endIndex, options:.ByWords) {
(substring, substringRange, enclosingRange, value) in
if let _subString = substring {
if longestWord == nil || longestWord!.characters.count < _subString.characters.count {
longestWord = _subString
}
}
}
Easy way to get all the words in sentence
var problem = "find the longest word in the problem description"
problem.enumerateSubstrings(in: (problem.range(of: problem))!, options: NSString.EnumerationOptions.byWords, { (substring, substringRange, enclosingRange, stop) -> () in
print(substring)
})
Easy way to get longest word in sentence
var longestWord = ""
problem.enumerateSubstrings(in: (problem.range(of: problem))!, options: NSString.EnumerationOptions.byWords, { (substring, substringRange, enclosingRange, stop) -> () in
if longestWord.characters.count < substring?.characters.count{
longestWord = substring!
}
})
print(longestWord)
Note:Code with reference to Swift 3.For lower version of there will be some syntax changes.Let me know if you need the same for lower versions.

Javascript First letter uppercase restlower of two lines "."

I want to first letter to be in upper case other in lower. But after ".", it must be upper again..
function firstToUpperCase( str ) {
return str.substr(0, 1).toUpperCase() + str.substr(1);
}
var str = 'prompt("Enter text to convert: ")
var Upcase = firstToUpperCase( str );
document.write(Upcase);
Here's a simplistic answer based on what you provided. It does not take whitespace into account following the period since you didn't mention that in the specs.
function firstToUpperCase(str) {
var parts = str.split(".");
for (i = 0; i < parts.length; i++) {
parts[i] = parts[i].substring(0, 1).toUpperCase() + parts[i].substring(1).toLowerCase();
}
return parts.join(".");
}
If you're trying to deal with sentences, something like this might be a little better, though it does not preserve exact whitespace:
function firstToUpperCase(str) {
var parts = str.split(".");
for (i = 0; i < parts.length; i++) {
sentence = parts[i].trim();
parts[i] = sentence.substring(0, 1).toUpperCase() + sentence.substring(1).toLowerCase();
}
return parts.join(". ");

CTRL+V unformatted on Tinymce

I am trying to copy html text and paste it into an unformatted way in TinyMCE by simply using CTRL + C, CTRL + V.
I have had a hard time to do this as TinyMCE constantly tries to keep the initial formatting. I am using Rails.
Would you know any work around this?
You may configure tinymce to strip all unwanted tags when pasting.
To do this use the tinymce config param paste_preprocess:
paste_preprocess : function(pl, o) {
o.content = window.strip_tags( o.content,'<p><div><ul><ol><li><br>' );
},
with the function strip_tags as follows:
// Strips HTML and PHP tags from a string
// returns 1: 'Kevin <b>van</b> <i>Zonneveld</i>'
// example 2: strip_tags('<p>Kevin <img src="someimage.png" onmouseover="someFunction()">van <i>Zonneveld</i></p>', '<p>');
// returns 2: '<p>Kevin van Zonneveld</p>'
// example 3: strip_tags("<a href='http://kevin.vanzonneveld.net'>Kevin van Zonneveld</a>", "<a>");
// returns 3: '<a href='http://kevin.vanzonneveld.net'>Kevin van Zonneveld</a>'
// example 4: strip_tags('1 < 5 5 > 1');
// returns 4: '1 < 5 5 > 1'
function strip_tags (str, allowed_tags)
{
var key = '', allowed = false;
var matches = []; var allowed_array = [];
var allowed_tag = '';
var i = 0;
var k = '';
var html = '';
var replacer = function (search, replace, str) {
return str.split(search).join(replace);
};
// Build allowes tags associative array
if (allowed_tags) {
allowed_array = allowed_tags.match(/([a-zA-Z0-9]+)/gi);
}
str += '';
// Match tags
matches = str.match(/(<\/?[\S][^>]*>)/gi);
// Go through all HTML tags
for (key in matches) {
if (isNaN(key)) {
// IE7 Hack
continue;
}
// Save HTML tag
html = matches[key].toString();
// Is tag not in allowed list? Remove from str!
allowed = false;
// Go through all allowed tags
for (k in allowed_array) { // Init
allowed_tag = allowed_array[k];
i = -1;
if (i != 0) { i = html.toLowerCase().indexOf('<'+allowed_tag+'>');}
if (i != 0) { i = html.toLowerCase().indexOf('<'+allowed_tag+' ');}
if (i != 0) { i = html.toLowerCase().indexOf('</'+allowed_tag) ;}
// Determine
if (i == 0) { allowed = true;
break;
}
}
if (!allowed) {
str = replacer(html, "", str); // Custom replace. No regexing
}
}
return str;
}
You may want to have a closer look at this SO-Thread too: TinyMCE Paste As Plain Text

C sharp delimiter

In a given sentence i want to split into 10 character string. The last word should not be incomplete in the string. Splitting should be done based on space or , or .
For example:
this is ram.he works at mcity.
now the substring of 10 chars is,
this is ra.
but the output should be,
this is.
Last word should not be incomplete
You can use a regular expression that checks that the character after the match is not a word character:
string input = "this is ram.he";
Match match = Regex.Match(input, #"^.{0,10}(?!\w)");
string result;
if (match.Success)
{
result = match.Value;
}
else
{
result = string.Empty;
}
Result:
this is
An alternative approach is to build the string up token by token until adding another token would exceed the character limit:
StringBuilder sb = new StringBuilder();
foreach (Match match in Regex.Matches(input, #"\w+|\W+"))
{
if (sb.Length + match.Value.Length > 10) { break; }
sb.Append(match.Value);
}
string result = sb.ToString();
Not sure if this is the sort of thing you were looking for. Note that this could be done a lot cleaner, but should get you started ... (may want to use StringBuilder instead of String).
char[] delimiterChars = { ',', '.',' ' };
string s = "this is ram.he works at mcity.";
string step1 = s.Substring(0, 10); // Get first 10 chars
string[] step2a = step1.Split(delimiterChars); // Get words
string[] step2b = s.Split(delimiterChars); // Get words
string sFinal = "";
for (int i = 0; i < step2a.Count()-1; i++) // copy count-1 words
{
if (i == 0)
{
sFinal = step2a[i];
}
else
{
sFinal = sFinal + " " + step2a[i];
}
}
// Check if last word is a complete word.
if (step2a[step2a.Count() - 1] == step2b[step2a.Count() - 1])
{
sFinal = sFinal + " " + step2b[step2a.Count() - 1] + ".";
}
else
{
sFinal = sFinal + ".";
}