I want to create a textarea where users can select a part of a text, and I will react according to their selection. So I need to
1) get the start and end positions of the selection text
2) get the position of the focus, if it is in the textarea and there is no selection
It seems that the functions to do so are different from an explorer to another. So could anyone tell me what is the approach to do that in Office Add-in?
I have tried the following 2 ways (ie, select a part of the text in myTextarea, click on button, and then debug the code), they don't seem to be the right functions.
(function() {
"use strict";
Office.initialize = function(reason) {
$(document).ready(function() {
app.initialize();
$('#button').click(showSelection);
});
};
function showSelection() {
// way 1
console.log(document.selection); // undefined
document.getElementById("myTextarea").focus();
var sel = document.selection.createRange(); // Uncaught TypeError: Cannot read property 'createRange' of undefined
selectedText = sel.text;
// way 2
console.log(document.getElementById("myTextarea").selectionstart); // undefined
console.log(document.getElementById("myTextarea").selectionend); // undefined
}
})();
Additionally, it would be great if one could also tell me how to realise the follows by code:
1) select a part of a text, from a start and end positions
2) set the focus at a certain position of the textarea
Edit 1:
I just tried window.getSelection() within my Excel add-in:
function showselection() {
var a = window.getSelection();
var b = window.getSelection().toString();
var c = window.getSelection().getRangeAt(0);
}
After selecting a text in the textarea, and clicking on button, I debugged step by step: the first line made a a = Selection {anchorNode: null, anchorOffset: 0, focusNode: null, focusOffset: 0, is ...; the second line returned "", the third line got an error Home.js:19 Uncaught IndexSizeError: Failed to execute 'getRangeAt' on 'Selection': 0 is not a valid index. It looks like the selection has not been successfully caught...
Here is JSBin without Excel add-in frame, which returns almost same results as above.
Use JQuery.
For instance, the following two lines get the caret position:
function showselection() {
console.log($('#myTextarea')[0].selectionStart);
console.log($('#myTextarea')[0].selectionEnd);
}
There are some plug-ins:
https://github.com/localhost/jquery-fieldselection
http://madapaja.github.io/jquery.selection/
The second one has several short samples with buttons (where we may lose selection). You could either use their API, or look into their code to see which JQuery functions they call.
If the desired selection is just the selected text in the HTML page (and not the user's selection in Excel/Word), then there are some good stackoverflow answers about accessing that selection.
One of the key features of the JavaScript APIs for Office is that they follow an asynchronous model (the code you've written above for showSelection() appears to be synchronous). I'd recommend reading the Excel and Word JS API overview pages to get a feel for how they work. As an example, here's how you'd get the text from a selection:
Word.run(function (context) {
var myRange = context.document.getSelection();
context.load(myRange, 'text');
return context.sync().then(function () {
log("Selection contents: " + myRange.text);
});
})
Then for the other specifics of your question please clarify as requested in my comment. Thanks!
-Michael (PM for Office add-ins)
Related
It has been 10 months since I worked on my app due to a death in the family, just started looking at it again and still not sure how to solve the problem.
The project inquires/help started here:
MFC MDI Collecting control states for the "apply" button routine
Since this is a specific focused question, I didn't want to muck up my other thread, so what I'd like to do is change the documents tab styles after the view is loaded. I know that this can be done because the master repository from Microsoft with all the code examples has a project called VCSamples-master\VCSamples-master\VC2010Samples\MFC\Visual C++ 2008 Feature Pack\TabControl which I have looked at. It dawns on me that even though I can follow its code, the calls are from within the MDI window itself where my issue is I'm trying to do this via a property page dialog using OnApply which changes things.
I was able to do part of this properly with the help of the thread above to the OutputPane successfully because I was able to get the Pane handle and execute. I was told that for the MDI tabs after creation that I need to parse the tabs, count them, and then execute. So my issue here is after I capture the tabs......how to change their styles.
Here is the code as it stands:
BOOL CSettingsUserTabs::OnApply()
{
BOOL bResult = CMFCPropertyPage::OnApply();
if (bResult)
{
// Update Output Pane Tab Styles (Works 100%)
AfxGetApp()->WriteProfileInt(_T("Settings"), _T("UserTabStyle"), m_style_tabs); // Save value to registry
((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.RecalcLayout();
//Get the open file tabs in the MDI
for (POSITION pos = AfxGetApp()->GetFirstDocTemplatePosition(); pos != NULL; )
{
CDocTemplate* pTempl = AfxGetApp()->GetNextDocTemplate(pos);
for (POSITION pos1 = pTempl->GetFirstDocPosition(); pos1 != NULL; )
{
CDocument* pDoc = pTempl->GetNextDoc(pos1);
for (POSITION pos2 = pDoc->GetFirstViewPosition(); pos2 != NULL; )
{
CView* pView = pDoc->GetNextView(pos2);
if (pView->IsKindOf(RUNTIME_CLASS(CTrainView)))
{
// THIS IS WHERE MY ISSUE IS, NOW THAT ALL THE TABS ARE
// CAPTURED, HOW DO I ADDRESS THEM LIKE WHAT IS SHOWN
// ABOVE:
//((CMainFrame*)AfxGetMainWnd())->xxxxxx.yyyyyy.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
}
}
}
}
}
return bResult;
}
If I can figure this last piece out, I'll be basically finished, I just can't seem to find a solution on how to do this via property sheet via OnApply.
Any suggestions or actual code examples I can see to solve my problem?
FYI: No, I haven't had any time to take additional OOP to solve this. I'm hoping someone can provide some guidance so I can move on after getting this sorted.
Thanks,
Chris
EDIT 1:
So I took a closer look at Constantine's suggestion and here is what I came up with:
BOOL CSettingsUserTabs::OnApply()
{
BOOL bResult = CMFCPropertyPage::OnApply();
if (bResult)
{
// Update Output Pane Tab Styles
AfxGetApp()->WriteProfileInt(_T("Settings"), _T("UserTabStyle"), m_style_tabs); // Save value to registry
((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
((CMainFrame*)AfxGetMainWnd())->m_wndOutput.m_wndTabs.RecalcLayout();
CMFCTabCtrl& MDI_STYLES = ((CMainFrame*)AfxGetMainWnd())->GetMDITabs();
MDI_STYLES.ModifyTabStyle((CMFCTabCtrl::Style)m_style_tabs);
MDI_STYLES.RecalcLayout();
CMDIFrameWndEx* pMainFrame = DYNAMIC_DOWNCAST(CMDIFrameWndEx, GetTopLevelFrame());
pMainFrame->SetFocus();
pMainFrame->RecalcLayout();
}
return bResult;
}
The m_styles_tabs is getting the index value of 0-8 when I select the radio button. The code compiles and runs and I see the index value change when I break on it, but the tabs for the MDI are still not updating. Does the edited code make sense based on the members shown here:
https://learn.microsoft.com/en-us/cpp/mfc/reference/cmfctabctrl-class?view=msvc-170#modifytabstyle
I think this the right direction, am I missing something?
I have a Tabulator (4.9.3) with values that use an editor of type text. As I tab through the table, I want each value to be selected so I can overwrite it without having to clear it first. I have tried putting the usual range selection code into the cellEditing callback and the Tabulator source code where the input gets created. Here is one variation of the code (I can't show them all because the node differs based on context):
try {
if (document.selection) {
// IE
var range = document.body.createTextRange();
range.moveToElementText(input);
range.select();
} else if (window.getSelection) {
var range = document.createRange();
range.selectNode(input);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
}
} catch (e) {console.log(e);}
If I double-click on the cell, the value selects as desired. How can I get this to work with keyboard navigation as well?
Since the editor is not assigned an id, finding a selector that could find it reliably was problematic without editing the source code. Since I was going to have to edit the source, I ended up adding the following line to the onRendered function of the input element under Edit.prototype.editors.
input.select();
Apologies for another question re this, but I've tried so hard to get this working (I'm fairly new to SharePoint, don't have extensive coding knowledge, but know HTML and generally alright at trouble shooting).
We are using SharePoint online and we have SharePoint Tiles. We have recently added a few more tiles and it's obviously not wrapping these tiles, thus having to scroll right to access some.
I have found the code for wrapping the tiles here and when editing the page source, it appears to be working... until I save it. The code I put in is stripped out when I next go to the content editor.
I've read a few pages on it and have tried things such as the content editor web part, but for the life of me cannot get it to work.
If anyone would know if there's a step to step guide to ensure wrapped tiles are saved, I may be able to get it to work.
Any help is greatly appreciated.
If it changes anything, we use SharePoint online that is part of our Office 365 account.
Add the following code into script editor web part in the page.
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
// Update this value to the number of links you want to show per row
var numberOfLinksPerRow = 6;
// local variables
var pre = "<tr><td><div class='ms-promlink-body' id='promlink_row_";
var post = "'></div></td></tr>";
var numberOfLinksInCurrentRow = numberOfLinksPerRow;
var currentRow = 1
// find the number of promoted links we're displaying
var numberOfPromotedLinks = $('.ms-promlink-body > .ms-tileview-tile-root').length;
// if we have more links then we want in a row, let's continue
if (numberOfPromotedLinks > numberOfLinksPerRow) {
// we don't need the header anymore, no cycling through links
$('.ms-promlink-root > .ms-promlink-header').empty();
// let's iterate through all the links after the maximum displayed link
for (i = numberOfLinksPerRow + 1; i <= numberOfPromotedLinks; i++) {
// if we're reached the maximum number of links to show per row, add a new row
// this happens the first time, with the values set initially
if (numberOfLinksInCurrentRow == numberOfLinksPerRow) {
// i just want the 2nd row to
currentRow++;
// create a new row of links
$('.ms-promlink-root > table > tbody:last').append(pre + currentRow + post);
// reset the number of links for the current row
numberOfLinksInCurrentRow = 0;
}
// move the Nth (numberOfLinksPerRow + 1) div to the current table row
$('#promlink_row_' + currentRow).append($('.ms-promlink-body > .ms-tileview-tile-root:eq(' + (numberOfLinksPerRow) + ')'));
// increment the number of links in the current row
numberOfLinksInCurrentRow++;
}
}
});
</script>
We can also use CSS style below in script editor web part in the page to achieve it.
<style>
.ms-promlink-body {
width: 960px;
}
</style>
My question is exactly opposite of this question. So what I'm trying is I'm trying to find a way to lose focus on a cell after user selects an item from the autocomplete combobox in that cell.
$input.autocomplete({
delay: 0,
minLength: 0,
source: args.column.options,
select: function (event, ui) {
$input.val(ui.item.label);
grid.getEditController().commitCurrentEdit();
return false;
}
});
I used this code to lose focus indirectly after finishing with editing. It works fine, however, the cell stays selected somehow.
grid.getEditController().commitCurrentEdit();
I also tried the code below to lose focus but it throws error everytime when I run the code.
grid.setActiveCell();
grid.setSelectedRows(-1);
After selecting an item from the autocomplete combobox, I want the grid to lose focus and select nothing on the viewport of the grid.
Thanks for your answers in advance.
Try calling grid.resetActiveCell().
There was a commit made to the master branch last week that might address your issue: Fix keyboard focus getting trapped when cell has tabbable elements.
You can achieve it as follows,
if (grid.getActiveCell()) {
var row = grid.getActiveCell().row;
var cell = grid.getActiveCell().cell;
grid.gotoCell(row, cell, false);
}
The current state
As from my link you can pick different regions on the map and everything seems to be working until you re-select a county you have selected before. Data values stored with each path decide if it isSelected or notSelected. I have no problem in changing the element data just clicked with this but I can't find a way of storing the last element selected in a way that I can change it's element data. Which means I first have to click on the previous county to set it's element data to notSelected
First I define var currentcountyselected = "";. This allows me to store the paths[arr[this.id]].name;. When I click on a new path I can make the last path fill change with $('#'+currentcountyselected).attr({fill: attributes.fill});
In Raphael's for loop I set obj.data('selected', 'notSelected'); so all path elements are set to notSeelected.
So what I need is some way to store the last path so I can change it's element data
This is the click function cleaned up from live example.
obj.click(function(){
if(this.data('selected') == 'notSelected')
{this.animate({fill: '#698B22' }, 300);
this.data('selected', 'isSelected');
$('#'+currentcountyselected).attr({fill: attributes.fill});
paths[arr[this.id]].value = "isSelected";
currentcountyselected = paths[arr[this.id]].name;
}
else
{this.animate({fill: '#32CD32'}, 300);
paths[arr[this.id]].value = "notSelected"; /* set path value*/
this.data('selected', 'notSelected');
}
});/* end mark selections */
I've been working on this project for a while and the client now wants the interface to work differently. This has really ate up my hourse.
EDIT:Although I have found a solution by simply taking out the if/else I would still like to know how to get at element data in a previous path (or any path for that matter).
Here is my solution, posted as it might help someone. The link in my question has problems with click happy users.
Globals
var previouscountyselected = "Mayo"; /* default start, can be any county(path) */
var start = true;
var past = null;
Changed code
obj.click(function(){
if(paths[arr[this.id]].value == 'notSelected')
{
this.animate({fill: '#698B22'}, 200);
paths[previouscountyselected].value = "notSelected";
paths[arr[this.id]].value = "isSelected";
previouscountyselected = paths[arr[this.id]].name;
if (!start && past != this)
{
past.animate({ fill: '#fff' }, 200);
}
past = this;
start = false;
}
else if(paths[arr[this.id]].value == 'isSelected')
{
this.animate({fill: '#32CD32'}, 200);
paths[arr[this.id]].value = "notSelected"; /* set path value */
}
});
Overview
if (!start && past != this) is a little unusual and is required or animated fades get messed up and choppy. The fade is not triggered if it is the first time a path is clicked and if you just hammer clicks on one path it doesn't fade to white. The main if/else handles the actual control value.
Until I get a jsfiddle up this link will demonstrate the desired behaviour.
Note! the drop menu in this link does not work.
Click happy friendly