Traversing through Word document using Office JS - javascript

I'm working on a Word Add-in using Angular, and I'm stuck at this point here.
I'm trying to traverse through the document searching for occurences of a word, but instead of performing an operation on all occurrences, I want to perform an operation on them by navigating to them and determine what to do with them one by one.
I have the following code:
goToNext(searchString: string){
return Word.run(wordContext => {
var results = wordContext.document.body.search(searchString, { matchWholeWord: true, matchCase: true }); //Search for the text to replace
results.getFirst().select();
return wordContext.sync();
});
}
This does select the first occurrence of the search, but calling the function again won't select the next occurrence.
How do I go to the next occurrence by calling that method again?

Wrote this in a comment, but marking this as the answer for others that might be interested
Word.run(wordContext => {
const search = wordContext.document.body.search(searchTerm, { matchWholeWord: wholeWord, matchCase: true });
wordContext.load(search);
return wordContext.sync().then(() => {
search.items[index].select();
});
This selects the word that is searched for based on where it is located in document reading the index value

Use Below function to search for a particular text under word document, make sure to pass correct wordIndex present under document for the word you would like to search
goToNext(searchString: string, wordIndex : string){
Word.run(async function(wordContext) {
var results = wordContext.document.body.search(searchString, { matchWholeWord: true, matchCase: true }); //Search for the text to replace
context.load(results);
return context.sync().then(() => {
search.items[wordIndex].font.highlightColor = '#ffedcc'; //If you want to highlight the selection with background color
search.items[wordIndex].select();
});
});
}

Related

Function that takes an input text, checks if it has certain elements, and returns an output value (JS)

I'm kind of new to JavaScript, and I hope someone can help me figure out how to develop an application that:
Takes a text value from an HTML input when pressing a button;
Check if that text value has certain syllables (e.g. if it has the syllables "ba", "ca", and "da" in it);
returns a value to the HTML output whether or not it has syllables.
Obs. HTML is no problem. My focus is on JS.
I know it sounds simple, but I would like at least a direction from where to start. I already realized that I will have to have at least two variables, one for the text value
const text = document.getElementById("name").value;
and another one for the syllables that I intend to check
constant syllable = ["ba", "ca", "da", "fa", "ra"];
Am I in the right direction? My problem starts when I try to write the functions. Anyway, I appreciate any help. Thanks.
you can use find on array and includes in a string
const syllable = ["ba", "ca", "da", "fa", "ra"];
validate = (word) => {
if (!word) {
return false;
}
let res = syllable.find(i => word.includes(i));
return res ? true : false
}
console.log(validate("aadaa"))
Your questions is a little vague, but I hope this answers it. I've included some explanation about how it all works.
// You define a function using 'function <identifier>(){<functionbody>}' (generally)
function containsSyllables(text_value, syllables) {
// parameters are set here ^^^^^^^^^^^^^
let foundSyllable = false; // <-- keep track of whether or not we've found a matching syllable
// this loops through each item in `syllables` (we refer to each item as `syllable`)
for (let syllable of syllables) {
// You can use the String.prototype.includes(substring) method to check if a string contains a substring
if (text_value.includes(syllable)) {
foundSyllable // <-- keep track that we've found a syllable in the text_value
break // exit this loop if we found a matching syllable
}
}
return foundSyllable
// return the variable that kept track of whether or not we found a syllable
}
var result = containsSyllables("bad", ["ca", "ba"]);
console.log(`"bad" contains "ca" or "ba"? ${result}`);
// OUTPUTS: "bad" contains "ca" or "ba"? false
There are some improvements you could make to this function, but I think this gets the point across simply.
If there is a line in this that you do not understand, you can search up roughly what it is: e.g. "for loop javascript". Look for a MDN link, they're the best.
You can take your element with getElementById, like you did, add event on it with a function name.addEventListener('input', updateValue); then check if your input includes one of your substrings from array and print a message as result ( check array.some)
const syllable = ["ba", "ca", "da", "fa", "ra"];
const name = document.getElementById("nameInput");
name.addEventListener('input', checkForSyllables);
function checkForSyllables(e) {
const value = e.target.value;
if (syllable.some(syllable => value.includes(syllable))) {
console.log(value) // your word contains one of elements from array
document.getElementById("result").innerHTML = value + " contains syllable";
}
}
<p> Name: </p>
<input type="text" id="nameInput" keyup="onKeyUp">
<p id="result" />

flitering multiple Datatables without using regex

I'm using Datatables 1.10.19 though I would happily upgrade if that will help.
I have an application with several tables on the same page. For one of those tables I want to offer the user the option to do a negative search. They enter a value and see all the rows that don't have that value.
So far as I can see the search() API allows simple text or regex criteria. I've seen examples such as this.
var allTables = $('table.dataTable').DataTable();
allTables.column( 0 ).search( 'mySearchTerm', true, false ).draw();
Some regex dialects support negative look ahead so regex such as those described in this answer would allow me to specify negation, but it appears that that the regex engine in use in Datatables does not work with such expressions.
My alternative is to use a filter function which I can establish with this:
$.fn.dataTable.ext.search.push()
However that seems to be a global construct affecting all tables, which I really don't want.
Any suggestions please?
You can use $.fn.dataTable.ext.search.push() - but it requires some additional work, to handle the fact (as you point out) that this is a global function, affecting all tables.
The following is not a full implementation, but shows the main points:
The Approach
My example uses 2 tables, with hard-coded data in each HTML table.
Each table is initialized as follows:
$(document).ready(function() {
// ---- first table with custom search ---------//
var table_a = $('#example_a').DataTable( {
pageLength: 10
} );
// remove the default DT search events - otherwise
// they will always fire before our custom event:
$( "#example_a_filter input" ).unbind();
// add our custom filter event for table_a:
$('#example_a_filter input').keyup( function( e ) {
table_a.draw();
} );
// ---- second table with DT default search ----//
var table_b = $('#example_b').DataTable( {
pageLength: 10
} );
} );
For my custom search function, I make use of the fact that the function includes a settings parameter which we can use to see which table is being searched:
$.fn.dataTable.ext.search.push (
function( settings, searchData, index, rowData, counter ) {
var tableID = settings.nTable.id;
var searchTerm = $('#' + tableID + '_filter input').val()
//console.log( tableID );
//console.log( searchTerm );
//console.log( searchData );
switch(tableID) {
case 'example_a':
if (searchTerm === '') {
return true;
} else {
show_me = true;
searchData.forEach(function (item, index) {
if (show_me && item.includes(searchTerm)) {
show_me = false;
}
});
return show_me;
}
break;
default:
// for all other tables, pass through the rows
// already filtered by the default DT filter:
return true;
}
}
);
The following line is where we ID which table is being filtered:
var tableID = settings.nTable.id;
After that, I can use a switch statement to handle each table's search separately.
In the default case (for example_b), I'm just passing through what was already filtered by the DT default search:
default: return true;
The above filter looks like this when I search each table for the letter x:
Incomplete Implementation
This logic is incomplete for the custom search logic. It assumes the search term is a single string. If I enter "x y" in the input field, that will exclude all records where a field contains "x y" - which is probably not what you want.
You probably want the exact negation of the standard search - so the input term would need to be split on spaces and each sub-term (x and y) would need to be checked separately across each row of data.

How to filter data with an array of strings matching at least one?

I'm quite new to ReactJS and work on a simple application which shows many different data (called apps). I want to implement a live free text search-filter. For that I'm using an Input Element, calling a JavaScript function if the value is changing. It's working quite good. But only for one string. If I want to filter for more words it's handled as an "AND" filter. But I want an "OR" filter.
I have the apps and filter them with a searchString. The User has to input at least three characters. If the user enters two words with three characters f.e. 'app' and 'red', I want to filter for the elements which has the words 'app' OR 'red' in the title. If min. one of the filter-strings matches, the app is shown. That's the plan.
I tried with .indexOf(), .includes() and nothing matches, even in the documentation I found nothing like an "OR" filter-search.
Here is my code, working for one string:
updateSearch(event) {
let searchString = event.target.value.toLowerCase()
let searchStringSplit = searchString.split(/(\s+)/).filter( function(e) { return e.trim().length >=3; } ); //Only words with at least 3 chars are allowed in the array
if (searchString.length >= 3) { //Check if the minimun amount of characters is fullfilled
let allApps = this.props.applications;
let apps = allApps.filter(app =>
app.title.toLowerCase().includes(searchString)
);
this.props.updateApplications(apps);
} else {
this.clearSearch()
}
}
my Input element:
<Input
id="freeTextSearch"
className="searchInput"
onChange={this.updateSearch.bind(this)}
autoComplete="off"
action={
<Button
id="freeTextSearchButton"
name="search"
icon="search"
onClick={() => this.clearSearch()}
/>
}
placeholder="Search"
/>
Thanks for the help
Yvonne
ANSWER:
Thank you 'Silvio Biasiol'. Your Solution gave me the right hint. Now I have an 'OR' filter-search matching at least one word. The function now looks like:
updateSearch(event) {
let searchString = event.target.value.toLowerCase()
let searchStringSplit = searchString.split(/(\s+)/).filter( function(e) { return e.trim().length >=3; } )
if (searchStringSplit.length >=1) { //Check if there is at least on word with tree letters
let allApps = this.props.applications
// If at least a word is matched return it!
let apps = allApps.filter(app => {
let containsAtLeastOneWord = false
searchStringSplit.forEach(searchWord => {
if (app.title.toLowerCase().includes(searchWord))
containsAtLeastOneWord = true;
})
if (containsAtLeastOneWord)
return app
}
);
this.props.updateApplications(apps)
} else { // user deletes manually every word
this.clearSearch()
}
}
Thanks at everyone
If you just want to match at least one word than it's pretty easy :)
let string = "My cool string"
let possibleStrings = [
'My cool string',
'My super cool string',
'Another',
'I am lon but sadly empty',
'Bruce Willis is better than Pokemon',
'Another but with the word string in it',
'Such string much wow cool']
// Split spaces
let searchString = string.toLowerCase().split(' ')
// Log the result, just wrap it in your react script
console.log(possibleStrings.filter(string => {
let containsAtLeastOneWord = false;
// If at least a word is matched return it!
searchString.forEach(word => {
if (string.toLowerCase().includes(word))
containsAtLeastOneWord = true;
})
if (containsAtLeastOneWord)
return string
}))
You are not using your searchStringSplit array. Using this array you could do the following:
const searchRegex = new RegExp(searchStringSplit.join("|"))
let apps = allApps.filter(app =>
searchRegex.test(app.title.toLowerCase())
);
You join your searchStringSplit array into a regex with the form term1|term2|term3... and match it aginst the title.
Another option would be to use the Array.prototype.some() function like this:
let apps = allApps.filter(app =>
searchStringSplit.some(searchString => app.title.toLowerCase().includes(searchString))
);
You fitler all your apps and for each app you check if it's title includes 'some' of the search strings.
trying to understand your code, suppose that searchString is "facebook twitter youtube"
let searchStringSplit = searchString.split(/(\s+)/).filter( function(e) { return e.trim().length >=3; } );
//searchStringSplit is like [ "facebook", "twitter", "youtube" ]
//allApps is like ["TWITTER","INSTAGRAM"]
allApps.filter(app=> searchStringSplit.includes(app.toLowerCase()))
//returning me all apps in allApps that are includes in searchStringSplit
// so return ["TWITTER"]
not sure if it's exactly what you need...if it's not please let me know so I can change the answer...

How to set goog.ui.Autocomplete minimum input to 0

I would like the autocomplete to show the entire list when the input box gets focused (no input is given). Would also like the auto complete to match substrings without having to fiddle with private variables.
At the moment the code is:
autocomplete = goog.ui.ac.createSimpleAutoComplete(
gsa.Game.gameData.teams, team2, false);
matcher=autocomplete.getMatcher();
matcher.useSimilar_=true
autocomplete.setMatcher(matcher);
Similar matches work but have to set a private variable for that (no getter or setter available).
The other one I have not been able to find out; how to show all data when no input is given (like a smart select input). So when the textbox receives focus it'll show all data since there is no filter text given. These are basic things that one would like to configure but can't find it in the API documentation.
You need to create descendants of goog.ui.ac.AutoComplete, goog.ui.ac.ArrayMatcher and goog.ui.ac.InputHandler. Also you will directly create the instance of auto complete object instead of calling goog.ui.ac.createSimpleAutoComplete.
In goog.ui.ac.AutoComplete descendant you assign custom input handler and matcher.
goog.provide('my.ui.ac.AutoComplete');
goog.require('goog.ui.ac.Renderer');
goog.require('my.ui.ac.ArrayMatcher');
goog.require('my.ui.ac.InputHandler');
my.ui.ac.AutoComplete = function(data, input, opt_multi, opt_useSimilar) {
var renderer = new goog.ui.ac.Renderer();
var matcher = new my.ui.ac.ArrayMatcher(data, !opt_useSimilar);
var inputhandler = new my.ui.ac.InputHandler(null, null, !!opt_multi, 300);
goog.ui.ac.AutoComplete.call(this, matcher, renderer, inputhandler);
inputhandler.attachAutoComplete(this);
inputhandler.attachInputs(input);
};
goog.inherits(my.ui.ac.AutoComplete, goog.ui.ac.AutoComplete);
In goog.ui.ac.ArrayMatcher descendant you need to override getPrefixMatches() method, since the default behaviour discards empty strings. So if there is an empty string, we just return the first x rows from the data.
goog.provide('my.ui.ac.ArrayMatcher');
goog.require('goog.ui.ac.ArrayMatcher');
my.ui.ac.ArrayMatcher = function(rows, opt_noSimilar) {
goog.ui.ac.ArrayMatcher.call(this, rows, opt_noSimilar);
};
goog.inherits(my.ui.ac.ArrayMatcher, goog.ui.ac.ArrayMatcher);
my.ui.ac.ArrayMatcher.prototype.getPrefixMatches = function(token, maxMatches) {
if (token == '')
{
// for empty search string, return first maxMatches rows
return this.rows_.slice(0, maxMatches);
}
else
{
return goog.base(this, 'getPrefixMatches', token, maxMatches);
}
};
In goog.ui.ac.InputHandler descendant you need to override processFocus() method, and force to show the autocomplete popup. This can be done by calling update() method with first parameter set to true.
goog.provide('my.ui.ac.InputHandler');
goog.require('goog.ui.ac.InputHandler');
my.ui.ac.InputHandler = function(opt_separators, opt_literals, opt_multi, opt_throttleTime) {
goog.ui.ac.InputHandler.call(this, opt_separators, opt_literals, opt_multi, opt_throttleTime);
};
goog.inherits(my.ui.ac.InputHandler, goog.ui.ac.InputHandler);
my.ui.ac.InputHandler.prototype.processFocus = function(target) {
goog.base(this, 'processFocus', target);
// force the autocomplete popup to show
this.update(true);
};

With Underscorejs, how to find whether an array contains another array?

I have this
var matches = bookmarks.filter(function(x) {
return _.contains(x.get("tags"), 'apple');
});
Which will return the bookmark objects that have the apple tags
I want to put an array there instead to pull and all the bookmarks that have the matching values, similar to this
var matches = bookmarks.filter(function(x) {
return _.contains(x.get("tags"), ['apple','orange']);
});
This doesn't work, any way to get it to work?
EDIT: Im sorry, bookmarks is a collection and im trying to return the models that have the apple and orange tags
If tags is a string, your code it would be
return _.indexOf(x.get("tags"), ['apple','orange']) > -1;
Example with indexOf : jsFiddle
If tags is an array, you can use intersection
return _.intersection(['apple','orange'], x.get("tags")).length > 0;
Example with intersection: jsFiddle
There doesn't seem to be a function for that in underscore. However, you can easily combine other functions to accomplish this:
_.mixin({
containsAny: function(arr, values) {
// at least one (.some) of the values should be in the array (.contains)
return _.some(values, function(value) {
return _.contains(arr, value);
});
}
});

Categories

Resources