I'm in the process of developing a Chrome extension, with the intention of providing "paste" functionality from Excel into a form/table in our ERP system (Sage ERP X3 V7).
I've got everything working reasonably swell, but I've run into issues getting the default events that fire when you manually input text, to fire when putting it in programmatically.
I've used 'Visual Event' to see what events listeners are assigned to the inputs, and tried manually triggering them using the jQuery .trigger method.
I'm not sure if it's just a timing issue or what? Normally when you manually enter lines, you'll hit tab to move to the next field, and it'll take a bit to validate your data before moving on. It's that validation I need to trigger here.
This is the code snippet I'm using to try and trigger the focusin and focusout events -
function pasteLineData(lineData, data_s_article){
console.log($(".s-list-core[data-s-article='" + data_s_article + "'] tbody>tr:last").length);
var fieldData = lineData.split("\t");
var f=0;
$(".s-list-core[data-s-article='" + data_s_article + "'] tbody>tr:last").each(function (){
var $fields = $(this).find("input[readonly!='readonly']:not(.s-readonly):visible");
$fields.each(function(){
console.log($(this).attr("id"));
console.log(fieldData[f]);
$(this).trigger("focusin");
$(this).val(fieldData[f]);
$(this).trigger("focusout");
f++;
});
});
}
(the selectors in use are just there to grab the specific table and only the active fields, a some are display only)
Any suggestions on how I can get my extension to simulate the entry process?
Related
I have got an old C++ MFC app upgraded to use the WebView2 web browser control, based on Edge. Otherwise I have got it working fine, one of the remaining issues is opening the Find dialog in it via the main MFC app Edit-Find menu item or via Ctrl+F (which is also intercepted by MFC framework). What I have now is:
m_webView->ExecuteScript(L"window.find(\"\", 0, 0, 0, 0, 0, 1);");
where m_webView is a pointer to ICoreWebView2 interface.
The problem is that sometimes it does bring up the Find dialog, sometimes not. Sometimes it does it after page refresh, sometimes not. When I close the Find dialog from cross, it typically refuses to open it again.
How can I make it behave properly? Maybe there is a way which does not need to go through Javascript?
Update: it appears it only worked if the Ctrl+F keystroke was somehow sent directly to the WebView2 control, bypassing MFC. It looks like the above Javascript line never worked. So maybe there is a way to simulate Ctrl+F for the WebView2 control?
AFAIK, WebView2 currently has no support for you invoking or otherwise controlling the find in page dialog. You can also refer to this thread. There is a similar thread in GitHub and the official hasn't given a solution.
You can use Ctrl+F keystroke directly in WebView2 control or provide feedback about this issue on WebView2 Feedback page. Thanks for your understanding.
As #yu-zhou answered, there is no official way yet to do what I wanted. For now I resorted back for emulating Ctrl+F programmatically for the WebView2 control, but the result ain't so pretty. For the record I still present it here:
First, the user has to click in the webview2 control at least once, so I can record its HWND in the GotFocus event handler registered by add_GotFocus():
void MyHtmlView::OnGotFocus() {
if (!m_webview2_hwnd) {
m_webview2_hwnd = ::GetFocus();
}
}
Now, when I want to bring up the Find bar programmatically, I need to ensure the correct window is focused and send it the Ctrl+F key. Alas, it appears the control checks the Ctrl key state in async way, so it gets a bit tricky, I need to alter the keyboard state for the thread and add an extra event handler for restoring it afterwards. After some trial and error I reached this (error checks omitted for brevity):
void MyHtmlView::Find() {
if (::GetFocus()!=m_webview2_hwnd) {
::SetFocus(m_webview2_hwnd);
}
std::uint8_t state[256];
::GetKeyboardState(state);
bool restoreCtrlState = false;
if ((state[VK_CONTROL] & 0x80)==0) {
// Ctrl key is currently not pressed.
// Mark it pressed.
state[VK_CONTROL] |= 0x80;
::SetKeyboardState(state);
restoreCtrlState = true;
}
std::thread auxThread([this, restoreCtrlState]() {
::SendMessage(m_webview2_hwnd, WM_KEYDOWN, 'F', 1);
::SendMessage(m_webview2_hwnd, WM_KEYUP, 'F', 1);
if (restoreCtrlState) {
::PostMessage(m_hWnd, WM_COMMAND, ID_RESTORE_CONTROL_STATE, 0);
}
});
auxThread.detach();
}
// This needs to be registered to handle ID_RESTORE_CONTROL_STATE
void HtmlView::OnRestoreCtrlState() {
std::uint8_t state[256];
::GetKeyboardState(state);
if ((state[VK_CONTROL] & 0x80)!=0) {
state[VK_CONTROL] &= ~0x80;
::SetKeyboardState(state);
}
}
Needless to say, this hack may easily cease to work if they change something in the webview2 implementation.
I am building a bigger application in React, JavaScript and HTMLusing Visual Studio Code.
I am using a third party router to log-in inside a small robot I built. This robot will turn on using a relay which is triggered by a combobox and a related button which will apply the combobox choice. Also I am new to JavaScript and HTML and for this reason I learned how to deploy a website and I prepared a minimal verifiable example which can be found here https://comboboxtest.netlify.com/ and here you can also get the source code if needed.
The goal of the application after I launch Visual Studio will be:
1) log-in inside the router,
2) automatically trigger the combobox and
3) automatically apply the combobox choice using the Apply button.
For better showing this see print screen below:
The problem: In order to access to the router there is an external interface that I didn't write because is from the router. I navigated through the interface and arrived to the Apply button as shown below in the HTML code:
So the operations are the following:
1) I trigger the relays using the combobox
2) using the mouse I have to hover on the Apply button
3) After the mouse is on the button (button hovered) I can click and apply the choice
4) The choice should be confirmed by the statement to become green.
What is not working: I can trigger the combobox, but I am not able to atomatically hover and click on the apply button.
Below the most important part of the code I am using:
property string get_relay_comboBox: "
var comboBox = document.getElementsByName('859-2-2')[0];
// find a target element.
if (comboBox) {
// Add event listener for combobox
comboBox.addEventListener('change', function (e) {
console.log('Change event detected:', e);
});
// Get new option index by flipping the current selected index
var newIdx = (comboBox.selectedIndex + 1) % 2;
// set the new index
comboBox.selectedIndex = newIdx;
// fire change event
comboBox.dispatchEvent(new Event('change'));
}
else {
console.error('comboBox not found!');
}
";
property string applyBtn: "
function fun('btnb btnhov') {
document.getElementById('btn_Apply').click();
}
";
What I tried so far
1) I research for a while the problem and I found this and also the following post which helped to understand how to trigger the combobox.
2) I right clicked on the router interface right on the Apply button and opened the Inspect Element menu and went to the Show DOM properties I found that maybe the following sreen-shot could be useful:
3) This I used to understand the HTML part and in fact I understood that it is important the way components should be called but still could't figure out how to accept the choice of the combobox using the button.
4) Following this source which is the basic way to call a clicked event I simply applied it as it is possible to show it below:
function fun('btnb btnhov') {
document.getElementById('btn_Apply').click();
}
But I didn't work and I think that at this point the most important problem as explained would be how to detect a mousover event and after that, push the button.
How to do that?
EDITS
I also tried to rewrite the function in the following way:
property string applyBtn: "
var btnApply = document.getElementById('btn_Apply');
if(btnApply) {
// Try add event listener for button
btnApply.addEventListener('change', function(e) {
console.log('Change event detected:', e);
});
// Push button
// Here the button should be pushed (or hovered first????)
}
else {
console.error('btnApply not found!');
}
";
Thanks to anyone who could please guide to the right direction to solving this problem.
EDIT - A short/brief description of the question. Is it possible using jQuery and javascript to intercept popup windows (on open) and listen/detect for keyboard input into text boxes from the parent window.
I am working on a site that has recently switched to Single Sign On type authentication. As a result, the user's session is no longer controlled by the application, but rather a central authorization server.
In order to prevent users from being booted without realizing their session is timing out, we created a simple timer. After X minutes of no activity, a warning banner appears with a link to extend the session.
Here is the code snippet we use to listen for activity on a web page
function DetectChanges() {
$('input[type=text], textarea').on('change keyup paste', function () {
console.log('Input detected - Resetting ession timers');
$('input[type=text], textarea').off('change keyup paste');
ResetSessionExpirationInformation();
setTimeout(DetectChanges, msToCheckInput);
});
}
Basically the premise is, every so often, see if the user is hitting the keyboard. If so, extend the session and stop listening for keyboard input for a little bit.
The problem I am running into is that I need to be able to tell if the user is hitting the keyboard in a child window (old schoool popup, not a dialog or modal). Since this site uses tons of inline JS, I don't have an easy way to gain access to these new windows. However, I did find that I can override the window.open function in order to track the windows.
var openedWindows = [];
window._open = window.open; // saving original function
window.open = function (url, name, params) {
var newWin = window._open(url, name, params);
newWin.focus();
newWin.onload = setTimeout(DetectChangesChild(newWin), msToCheckInput);
ResetSessionExpirationInformation();
openedWindows.push(newWin);
I am passing the reference to the window in the above DetectChangesChild method so I have a reference later on the code path.
function DetectChangesChild(childWindow) {
childWindow.alert("Listening Child Window");
$(childWindow).contents('input[type=text], textarea').on('change keyup paste', function () {
alert('Stuff happened');
console.log('Input detected - Resetting session timers');
childWindow.$('input[type=text], textarea').off('change keyup paste');
ResetSessionExpirationInformation();
setTimeout(DetectChangesChild(childWindow), msToCheckInput);
});
}
Does anyone know how to listen on text input elements in a popup for changes so that I can reset the session expiration timers appropriately?
Note - I am aware of the cautionary statements against overriding native functions, but we are in no danger of colliding with another override of this function.
I have setup a page where multiple instances of the jQuery File Upload plugin can be added by clicking a button. The problem I'm having is when you drag files to one of the instances, it adds them to all. I've added the code suggested by the developer to keep this from happening, but it seems to have no effect (though the function is called, I have it logging to console.)
Here's the page I'm working on:
http://bwobst.clients.thomporter.com/upload.html
Here's the JavaScript file that creates the uploaders:
http://bwobst.clients.thomporter.com/upload.js
Or if you prefer, the CoffeeScript:
http://bwobst.clients.thomporter.com/upload.coffee
FYI, this is the bit of code the developer suggests to use:
$(document).bind('drop dragover', function(e) {
e.preventDefault();
});
As I said, I tried adding a console.log to it, and it did indeed get called when I dragged files to the window, but the e.preventDefault() seems to have had no effect.
as said in the documentation you have to specify a dropZone, otherwise its the whole document.
you should change this line:
$('#uploader' + uploaderCount).fileupload();
to
$('#uploader' + uploaderCount).fileupload({dropZone:$('#uploader' + uploaderCount)});
Some form validators block CTRL+C/CTRL+V on some input fields. Is there a quick bookmarklet or something that could allow me to use my keyboard properly. Or, perhaps, browsers allow my to override that kind of rude behavior of some pages?
EDIT: What I'm looking for is similar solution like the one that can be used to enable right-click on a page by executing a javascript snippet withing context of the page:
javascript:void(document.oncontextmenu=null)
So far, I think that there is no similar solution with javascript snippet without involvment with 3rd party tools like Greasemonkey
That depends on how it is done. Also, removing/disabling event listeners is always risky, you might harm the functionality of the site seriously - and you never know whether a handler does evil or good things, least you can know whether there is a handler at all.
So, I've skimmed the first few Google results for "javascript prevent copy paste" and found mostly foolish scripts: Disable pasting text into HTML form, Prevent Copy, Cut and Paste into HTML Input Text Fields, not working example. So what a script could do now:
remove all on-copy, -paste, -input, -cut, -drag, etc properties of input elements
capture these event types on the document and stop their propagation, so (bubble) handlers will never get called and can't prevent the default action
Yet, as already said, this can't hinder everything. The input event, a very useful one, is cleverly used by this solution to prevent inserting more than one character at once. So you would need to decide whether you want to stop that event and possible break the application.
var evts = ["copy", "paste", "cut", "drag", "selectionstart?", "input?", …?];
var els = [document, document.body].concat(
[].slice.call(document.getElementsByTagName("input")),
[].slice.call(document.getElementsByTagName("textarea"))
);
function trap(e) { e.stopPropagation(); }
for (var i=0; i<evts.length; i++) {
var evt = "on"+evts[i].charAt(0).toUpperCase()+evts[i].substr(1);
for (var j=0; j<els.length; j++)
els[j][evt] = null; // remove handler
document.addEventListener(evts[i], trap, true); // capture phase
}
Another solution on windows: AutoHotkey.
SendRaw, %Clipboard%
For example, the following binds to ctrl+F3 to type out the clipboard contents. Just put this in a file called typeclipboard.ahk and run it with AutoHotkey:
^F3::SendRaw, %Clipboard%