How to select a specific element inside editor (tinymce 4.x) - image

I have an image inside the editor using plugin 'imagetools' and added an additional image operation on it (that works fine). After this custom operation is done I'm loosing the selection of the image, that I try to select again.
Since the image is initally selected I would have the chance to capture some information to select it again after custom image operation. But whatever I try it doesn't work:
Before operation (while the image is selected by user):
var node = tinymce.activeEditor.selection.getNode();
After operation:
tinyMCE.activeEditor.dom.select(node);
-> nothing selected
tinyMCE.activeEditor.selection.select(node);
-> Error: Argument 1 ('refNode') to Range.setStart must be an instance of Node
I assume the solution is pretty simple. I just don't get it and tinymce documentation is not really helpful on this.

Found it: You can set a bookmark before doing any changes on or inside the element:
var bookmark = tinymce.activeEditor.selection.getBookmark();
After element processing you set back the bookmark:
tinymce.activeEditor.selection.moveToBookmark(bookmark);
Now your previously selected element should will be selected again.

Related

Image as cell note in Google Sheets, to be displayed at mouseover/hover

I'd like to attach images to certain cells of my Google spreadsheet, to appear upon hovering the mouse upon that cell, similar to how the native text-only regular cell notes (Shift+F2) appear.
Natively, Google Sheets supports inserting images in the cell. My image sizes are however too big, and I'd have to make the row/column width/height huge for them to be displayed at 100%. Plus, I'd want the images to only appear upon mouseover, and not always be visible.
(I should add that the functionality I describe is actually very easily achievable in Excel, which allows setting a picture-background for cell comments, something that Google Sheets does not.)
I discovered a Google Sheets addon which seems to extend regular cell notes to include richer content, including images. Inconveniently, it requires each image be uploaded to an image server rather than be loaded from the PC. That would have still been fine, except I couldn't get it to achieve the above stated goal.
Finally, I found this suggested workaround, which for me does not work, in the sense that the image is not loaded as a preview upon mousing over the URL (whether it ends with .jpg or not), only the URL itself is:
Interestingly, the effect I'm after actually exists in Google Docs, when the link isn't even an image but just a page:
Issue:
There is no native way to trigger actions on hover events in Sheets, and there is no way to retrieve images that have been uploaded from the computer and don't have a valid URL. This greatly limits what you can accomplish.
Nevertheless, there are workarounds available that can get you close to the functionality you're asking for.
Workaround (showModelessDialog):
You could, for example create a modeless dialog whose purpose would be to show the image corresponding to the selected cell.
Ideally, onSelectionChange trigger would be used, since this trigger to refresh the modeless dialog with current image when the user changes the selected cell, but since creating a modeless dialog requires authorization, simple triggers cannot run services that require authorization, and onSelectionChange is only available as a simple trigger (cannot be installed), that won't be possible.
So one possible workflow could be the following:
Show a modeless dialog when the spreadsheet is opened by a user.
The user selects the cell with the image that should be shown.
User clicks a button in the modeless dialog (or the previous image) to refresh the modeless dialog with the currently selected image.
How to do this:
To achieve this, you can follow these steps:
Install onOpen trigger that creates modeless dialog when the user opens the spreadsheet (the trigger needs to be installed, since, as I mentioned, simple triggers cannot use services that require authorization). To do that, go to the Apps Script editor (selecting Tools > Script editor), copy this function to your Code.gs file, and install the trigger following these steps:
function onOpenTrigger(e) {
var html = HtmlService.createTemplateFromFile("Page").evaluate()
.setWidth(800) // Change dimensions according to your preferences
//.setHeight(600) // Change dimensions according to your preferences
SpreadsheetApp.getUi()
.showModelessDialog(html, "My image");
}
Create the HTML template corresponding to the modeless dialog. The idea here would be: when the page loads, the currently selected image is shown. If the button (or the image itself) gets clicked, the page will refresh with current image (see retrieveImage and refreshImage). Create an HTML file in the editor via File > New > HTML file, and copy the following code:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body onload="retrieveImage()">
<img id="currentImage" onclick="retrieveImage()" alt="No image is selected" width="600">
<button onclick="retrieveImage()">Click to refresh image!</button>
</body>
<script>
function retrieveImage() {
google.script.run.withSuccessHandler(refreshImage).getSelectedImage();
}
function refreshImage(imageUrl) {
if (imageUrl) document.getElementById("currentImage").src = imageUrl;
}
</script>
</html>
Back in your script file (.gs), create a function to retrieve the image URL from current cell (in case this cell has an image). This function will get called by the modeless dialog when it loads and when its button is clicked. It could be like this (regex is used to retrieve that — see this answer:
function getSelectedImage() {
var formula = SpreadsheetApp.getActiveRange().getFormula();
var regex = /=image\("(.*)"/i;
var matches = formula.match(regex);
return matches ? matches[1] : null;
}
Note:
You can adapt the dimensions of both the selected image (<img width="" height="">) and the modeless dialog (.setWidth, .setHeight) according to your preferences.
Reference:
Dialogs and Sidebars in G Suite Documents
Installable Triggers: Managing triggers manually
getActiveRange()

react: copy component state value to clipboard without dummy element

In my project there is one usage case: user click one button and then copy some data to clipboard for next step.
The copied data is related to the clicked button, and is stored in the component state.
I do some search, and find the potential solution as following:
function copyToClipboard(text){
var dummy = document.createElement("input");
document.body.appendChild(dummy);
dummy.setAttribute('value', text);
dummy.select();
document.execCommand("copy");
document.body.removeChild(dummy);
}
to some extend, we need to create a dummy element, set the copied data to the dummy element and select the element, then execute the execCommand(copy) method.
is it possible to do this without creating dummy element? I know there are some react plugin about clipboard, but I just want to use vanilla javascript. thank you
Your solution works well.
If the value you want to copy is not yet rendered on the DOM, your Document.createElement('input')... method is a good way to create a document node that Document knows about, but that is not visible to the user. Once you use .createElement() you can then call execCommand() on it to copy the value to the clipboard.
The execCommand() method is exposed by HTML5's Document. This means Document has to know about the node you are targeting before you can use the method (this is called Selection).
However, if you want to copy text from an element already rendered on the dom (e.g an input in a form), you could use React's callback ref. Here's a good example of using ref to do this. It's pretty simple, so using a library is likely to be overkill.

Link directly to a notebook page in a view

I have an view that extends the current project view, where we add multiple tabs (notebook pages) to show information from other parts of a project.
One of these pages is an overview page that summarizes what is under the other tabs, and I'd like to link the headlines for each section directly to each displayed page. I've currently solved this by using the index of each tab and calling bootstrap's .tab('show') method on the link within the tab:
$(".overview-link").click(function (e) {
e.preventDefault();
var sel = '.nav-tabs a:eq(' + $(this).data('tab-index') + ')';
$(sel).tab('show');
});
This works since I've attached a data-tab-index="<int>" to each header link in my widget code, but it's brittle - if someone adds a tab later, the current indices will be broken. Earlier I relied on the anchor on each tab, but that broke as well (and would probably break if a new notebook page were inserted as well).
Triggering a web client redirect / form link directly works, but I want to show a specific page in the view:
this.do_action({
type: 'ir.actions.act_window',
res_model: 'my.model.name',
res_id: 'my.object.id',
view_mode: 'form',
view_type: 'form',
views: [[false, 'form']],
target: 'current'
});
Is there any way to link / redirect the web client directly to a specific notebook page tab through the do_action method or similar on FormWidget?
If I understood well you want to select the tab from the JavaScript (jQuery) FormWidget taking into account that the id could change if anybody install another module that adds another tab
Solution 0
You can add a class to the page in the xml form view. You can use the id of the element selected by this class name in order to call the right anchor and select the right tab item. This should happen when the page is completely loaded:
<page class="nb_page_to_select">
$('a[href=#' + $('.nb_page_to_select').attr('id') + ']').click()
NOTE: As you have said the following paragrah I assume that you know where to run this instruction. The solution I suggest is independent of the index.
This works since I've attached a data-tab-index="<int>" to each
header link in my widget code, but it's brittle - if someone adds a
tab later, the current indices will be broken. Earlier I relied on the
anchor on each tab, but that broke as well (and would probably break
if a new notebook page were inserted as well).
Solution 1
When the page is loaded you can get the tab list DOM object like this:
var tablist = $('ul[role="tablist"]')
And then you can click on the specifict tab, selecing by the text inside the anchor. So you don't depend on the tab index:
tablist.find('a:contains("Other Information")').click()
I think if you have two tabs with the same text does not make any sense, so this should be sufficient.
Solution 2
Even if you want to be more specific you can add a class to the notebook to make sure you are in the correct notebook
<notebook class="nt_to_change">
Now you can use one of this expressions in order to select the tab list
var tablist = $('div.nt_to_change ul.nav-tabs[role="tablist"]')
// or
var tablist = $('div.nt_to_change ul[role="tablist"]')
Solution 3
If the contains selector doesn't convince you because it should be equal you can do this as well to compare and filter
tablist.find('a').filter(function() {
return $.trim($(this).text()) === "Other Information";
}).click();
Where "Other Information" is the string of the notebook page
I didn't tried the solution I'm giving to you, but if it doesn't work at least may be it makes you come up with some idea.
There's a parameter for XML elements named autofocus (for buttons and fields is default_focus and takes 1 or 0 as value). If you add autofocus="autofocus" to a page in XML, this page will be the displayed one when you open the view.
So, you can try to add this through JavaScript, when the user clicks on the respective link -which honestly, I don't know how to achieve that by now-. But you can add a distinctive context parameter to each link in XML, for example context="{'page_to_display': 'page x'}". When you click on the link, I hope these context keys will arrive to your JS method.
If not, you can also modify the fields_view_get method (here I wrote how to do that: Odoo - Hide button for specific user) to check if you get the context you've added to your links and add the autofocus parameter to the respective page.
As you said:
This works since I've attached a data-tab-index="" to each header
link in my widget code, but it's brittle - if someone adds a tab
later, the current indices will be broken.
I assume that your app allow multi-user interaction in realtime, so you have to integrate somewhere in your code, an update part function.
This function will trig if something has changed and cleanout the data to rebuilt the index in order to avoid that the current indices will be broken.

Make a click event based on a control's name value property

I'm using TestComplete for the automation of QA process.
TestComplete detects click events based on the postion of the mouse on the screen. But when I run the same test on a different machine (a larger/smaller screen) the mouse button won't click on the correct position.
Therefore I want to know is there a method which I can search a control based on its name (i.e. name of a button) and then run a script to click on that particular word (or position).
Any help is appreciated :)
Additional Details
This is a sample script generated by TestComplete for clicking the search button on a web page.
Sub Test6()
'Opens the specified URL in a running instance of the specified browser.
Call Browsers.Item(btIExplorer).Navigate("http://www.simplyrecipes.com/search/?cx=003084314295129404805%3Ai2-mkfc4cai&cof=FORID%3A11&q=&sa.x=48&sa.y=16")
'Clicks at point (173, 10) of the 'textboxQ' object.
Call Aliases.browser.pageHttpWwwSimplyrecipesComSearc.panelPage.headerMasthead.group.panelSiteSearch.formSearchbox0030843142951294048.textboxQ.Click(173, 10)
'Sets the text 'asd' in the 'textboxQ' text editor.
Call Aliases.browser.pageHttpWwwSimplyrecipesComSearc.panelPage.headerMasthead.group.panelSiteSearch.formSearchbox0030843142951294048.textboxQ.SetText("asd")
'Clicks at point (57, 12) of the 'imagebuttonSa' object.
Call Aliases.browser.pageHttpWwwSimplyrecipesComSearc.panelPage.headerMasthead.group.panelSiteSearch.formSearchbox0030843142951294048.imagebuttonSa.Click(57, 12)
End Sub
Here is the button click event captured in the RecordTest window
What I want to know is, is there a way I can specify the control name (i.e 'Search' or 'imagebuttonSa'), so the TestComplete will search for the name in the given GUI and then once it's found, make a click event on the word.
I'm using Windows 7 64bit OS, TestComplete 9 trial version and VBScript as the scripting language.
As far as I can see, TestComplete already works in your case with specific controls/objects: textboxQ and imagebuttonSa. These two names are given to objects by the Name Mapping functionality and you are free to change them as you like.
As for the coordinates, these coordinates are related to the top left corner of the corresponding control (textboxQ or imagebuttonSa). If you want, you can remove the coordinates from the action parameters (e.g. ...imagebuttonSa.Click()) and TestComplete will click the central point of the control.
In case you have any problems with playing back your tests and you cannot find out what the cause of these problems is, the best option for you will be contacting the SmartBear support team with detailed explanation of what happens. Send them your project with failed results logs.
Whenever I have a problem with not finding an object with TC I use one of this functions or a combination of them.
In case you have the table object (parent of the cell you want to click):
function clickCellByText(text,tablePath)
{
var table = tablePath;
if (!table.Exists)
{
Log.Error("A table with the following text cannot be found: " + table);
}
clickCell1(table, "*Cell*", "*Cell*");
function clickCell1(table, idText, linkText)
{
var propArray = new Array("innerText", "ObjectType");
var valArray = new Array(text,"Cell*");
var idCell = table.FindChild(propArray, valArray, 20000);
if (idCell.Exists)
{
// Row found, find the link in the same row
idCell.Click();
}
else
{
Log.Error("A cell with the following text cannot be found: " + text);
}
}
}
If you want to find the object or the parent of the object by any property:
function clickComponent(property,text)
{
// Using the Find method
var myObj = Sys.Browser("iexplore").Page("*").NativeWebObject.Find(property,text);
// Checks if the button exists
if (myObj.Exists)
{
myObj.Click();
}
else
Log.Error("Can't find the object","");
}
If you don't specify co-ordinate, it will click at the center of the object.
You can try doing FindChild or Find with the Object Name Mapping where the button resides, with Properties like (Eg:ObjectType as Button and Button Caption with your required button caption), once the FindChild finds the button with above specified properties you can use that object to click on it:
var button = ParentObj.FindChild(["ObjectType","Caption"],["Button","OK"],10);
if(button.Exists)
button.Click()

How to open and handle richtext editor in javascript in sitecore 6.5?

I've been working on a custom field, which contains a list.
I have to be able to edit the selected item on the list in a richtext editor. (this is the only missing part).
I've read the topic on opening from c# code Opening Rich Text Editor in custom field of Sitecore Content Editor .
This works nice for the "add" button, since i have to open the RTE empty(with default text...), but not for the Edit button.
My aproaches are:
Somehow in the Edit button's message field list:edit(id=$Target) pass the selected index (like list:edit(id=$Target,index=$SelectedIndex), but i don't know how to populate $SelectedIndex
Somehow in the overridden HandleMessage method get the list's selected index. I'm able to get the selected value Sitecore.Context.ClientPage.ClientRequest.Form[ID of list], but thats alone not much of a help, since i won't be able to decide which one to edit if two listitem equals.
Do the richtext editor opening and handling fully in javascript. As i saw at some script in content editor, i tried to do that, but i can't understand it clearly:
richtext editor url:
var page = "/sitecore/shell/Controls/Rich Text Editor/EditorPage.aspx";
some params :
var params = "?da=core&id&ed=" + id + "&vs=1&la=en&fld=" + id + "&so&di=0&hdl=H14074466&us=sitecore%5cadmin&mo";
and the part where i'm not sure:
var result = scForm.browser.showModalDialog(page + params, new Array(window), "dialogHeight:650px; dialogWidth:900px;");
This way the RTE opens as expected (i guess i could get the selected index from javascript and pass it as a parameter later). However when i click ok, i get exception from EditorPage.js saveRichText function: Cannot read property 'ownerDocument' of null. Am i missing some parameter?
Either of the three aproaches is fine for me(also i'm open for new better ones) as soon as i'm able to do it.
Thanks in advance!
Tamas
I was able to enter some javascript into the message:
list:Edit(id=$Target,index='+document.getElementById(ID of the select using $Target ).selectedIndex+')
this way i got the index in HandleMessage.
I'm waiting for better solutions now.

Resources