Identify whether print dialog box closed or printing completed - javascript

My requirement is to let the user print a document and update the database status. The user usually prints document as a PDF using Microsoft Print to PDF. I need to identify whether either user clicked the print button and saved the PDF or closed it without printing. Also, I need to redirect the user after a successful print.
I tried to add element.onafterprint event but it seems it returns the same response in both cases.
Also, with the above PDF printer, element.onafterprint event triggers after the user clicks the Print button, but there is a Save dialog box. So webpage redirects to the other page before the user completes the full action.
Is there a way to fix this? My current (not working properly) approach is as follows.
PS: My project is written in React
const printDoc = () => {
const divContents = document.getElementById('printArea').innerHTML;
const element = window.open('', '', 'height=700, width=1024');
element.document.write(divContents);
element.onafterprint = (e) => { // this is where things not work
console.log(e)
element.close();
updatePickListPrintStatus(PRINT_STATUS.SUCCESS);
redirectAfterPrint()
}
getCssFromComponent(document, element.document);
element.document.close();
// Loading image lazy
setTimeout(() => {
element.print();
}, 1000);
};

Related

Document ready event of another location opened using window.location in javascript

I am using below code to open a link on button click. The link is pointing to a Controller method responsible for downloading some Excel file.
// Button to download table data
$("#btnDownloadCIRResults").click(function (e) {
var All_Recs = $("#cbShowAllRecords").prop("checked") ? "YES" : "NO";
DisplayStatusMessageWind("Downloading report, please wait...", MessageType.Info, true, false);
// DownloadCIRemediationTable(string AllRecords)
window.location = '/AskNow/DownloadCIRemediationTable?AllRecords=' + All_Recs;
DisplayStatusMessageWind("Report downloaded successfully.", MessageType.Success, false, true, 1000);
e.preventDefault();
});
The Controller method queries a DB table, converts it to an Excel workbook and returns a file as download result. All is working fine and as expected, except, since this is a time consuming process, I just want to improve on user experience and update this code to show some wait message while the file is being downloaded.
The DisplayStatusMessageWind() method shows a wait message. However, it doesn't know or care about the load complete event of the window.location = '/AskNow/DownloadCIRemediationTable?AllRecords=' + All_Recs; code.
How can I make the completion message appear only after the file download is completed:
DisplayStatusMessageWind("Report downloaded successfully.", MessageType.Success, false, true, 1000);
By assigning a new location with window.location = "<NEWURL>"; you're requesting asynchronously to replace the current page. What will happen, is that the next line is immediately executed (DisplayStatusMessage()). When all events are handled, the page will finally be replaced. The new page (URL) will load and you'll have no control whatsoever about how or what will happen next.
What you should do is use window.open("<NEWURL>", '_blank') MOZ and then on the new page send a signal via localStorage, which can be read and written by all pages of the same domain. These are some hints, to write the actual code is your job.
On this page, in on("click") event:
// local scope
var ukey;
// polling function
function waitOtherIsReady()
{
if (localStorage.getItem(ukey) === true)
{
// other page experienced ready event
localStorage.removeItem(ukey); // clean-up
// TODO: do your stuff
} else {
setTimeout(waitOtherIsReady, 500);
}
}
// create unique key and deposit it in localStorage
ukey = "report_" + Math.random().toString(16);
localStorage.setItem(ukey, false);
// pass key to other page
window.open("URL?ukey=" + ukey, "_blank");
// start polling until flag is flipped to true
setTimeout(waitOtherIsReady, 500);
On the other page:
$(() => {
// get ukey from URL
var ukey = new URL(window.location.href).searchParams.get("ukey");
// page is now ready, flip flag to signal ready event
localStorage.setItem(ukey, true);
});

Get Puppeteer Page/Frame Handle for new page after `ElementHandle.click()`

Using puppeteer, I have a specific page that I am web-scraping for data and screenshot-ing for proof that the data is correct. The web page itself includes a button for creating a printer friendly version of the page. The button itself is implemented as an input of type button with no target attribute. Still, once clicked, the button opens the printer friendly version on a new page(tab) at about:blank that automatically opens up chrome's print dialog.
Whenever a new page opens up, I've typically done browser.waitForTarget() to try to capture the new target and work from there. The issue is that with any variation of code, I'm never able to find a Page that matches the page that was opened up. The closest I get is finding a Target of type other and a url of chrome://print.
Is there any way to find this type of target easily and even more get it's page (since target.page() only returns a page if the target.type() === 'page'? As a bonus, I'd like a way to potentially dismiss or ignore the window's print dialog, possibly even cancel.
You need to do the following to capture a new browser window:
const browser = await puppeteer.launch({
headless: false,
});
const page = await browser.newPage();
let page1;
browser.on("targetcreated", async (target) => {
if (target.type() === "page") {
page1 = await target.page();
}
});
Or you can find the desired page using browser.pages() method. See the documentation for more information.

Disable tabs opened in selenium (Node.js)

Im using the selenium webdriver for node.js and im also loading an extension, loading the extension works fine but when I start my project it goes to the page I want then instantly the extension opens a new tab (Thank you for adding this extension bla bla bla), Im wondering if theres a way to disable tabs that are not opened by myself, ive tried this:
await driver.get('https://mywebsite.com') //open my initial site
await driver.sleep(1000) //give time for the extension site to open
driver.switchTo(await driver.getAllWindowHandles()[1]) //switch to extension site
await driver.close()
driver.switchTo(await driver.getAllWindowHandles()[0]) //switch back to the main site
//rest of my code
Unfortunately this just does not seem to work, any advice appreciated!
There's no way to disable tabs not opened by your script. As long as you don't change window handles, the driver will still be on the original tab. You can proceed with the script from there, ignoring the other opened tabs.
I think the main issue I see with your code is that you are passing parameters to .switchTo() instead of .window(). It should be driver.switchTo().window(handle);.
If you want to find the new window to close it, I wrote that code in this answer. All you need to do is to add the .close() line after that code and switch back to the original handle, which you already have in your current code (after fixing with my feedback above).
Another approach is heavily based on the selenium.dev docs:
// Open the initial site
await driver.get('https://mywebsite.com')
// Store the ID of the original window
const originalWindow = await driver.getWindowHandle();
// Wait for the new window or tab
await driver.wait(async () => (await driver.getAllWindowHandles()).length === 2, 10000);
// Loop through until we find a new window handle
const windows = await driver.getAllWindowHandles();
windows.forEach(async handle => {
if (handle !== originalWindow) {
await driver.switchTo().window(handle);
}
});
await driver.close()
await driver.switchTo().window(originalWindow);
// Rest of the code

Chrome Extension: Clear Chrome Local Storage on Page Reload (not on Update)

I'm working on a Chrome Extension that will sort a DOM structure. On the extension popup I'm using a button that will activate / desactivate the sort. In order to save the state of my button I'm saving via Chrome Local Storage the "state" of my button this way:
function save_button_state() {
var buttonStateText = $("#js-toggleSorting").html();
var buttonStateAttribute = $("#js-toggleSorting").attr("data-click-state");
var sortMessage = $(".message").html();
chrome.storage.local.set(
{
buttonStateText: buttonStateText,
buttonStateAttribute: buttonStateAttribute,
sortMessage: sortMessage,
},
function () {
console.log(
`Saved State is: ${buttonStateText} and Saved Attribute is: ${buttonStateAttribute} and Saved Message is: ${sortMessage}`
);
}
);
}
That save the dom node and keep the information in order to save it when the popup is closed. Then, in order to get that info back from the local storage I'm using this function:
function get_button_state() {
chrome.storage.local.get(
["buttonStateText", "buttonStateAttribute", "sortMessage"],
function (data) {
$(".message").html(data.sortMessage);
$("#js-toggleSorting").html(data.buttonStateText);
$("#js-toggleSorting").attr(
"data-click-state",
data.buttonStateAttribute
);
console.log(
`Get State is ${data.buttonStateText} and Get Attribute is ${data.buttonStateAttribute}`
);
}
);
}
And then when the document is ready I'm processing the button onclick event changing the dom from "Sorting" to "Not Sorting" this way:
$(document).ready(() => {
get_button_state();
//Some Code to pass parameters from the extension to a content script
$("#js-toggleSorting").on("click", function () {
$(".message").html("");
if ($(this).attr("data-click-state") == 1) {
$(this).attr("data-click-state", 0);
$(this).html("SORT INCOMING CHATS");
$(".message").append("<p>STATUS: NOT SORTING CHATS</p>");
sortFunction(false);
} else {
$(this).attr("data-click-state", 1);
$(this).html("STOP SORTING INCOMING CHATS");
$(".message").append("<p>STATUS: SORTING CHATS</p>");
sortFunction(true);
}
save_button_state();
});
});
Then in the Chrome Extension background js file I'm trying to clear the local storage when I reload the page (the same page, with the same URL):
chrome.tabs.onUpdated.addListener(function (changeInfo) {
if (changeInfo.url === undefined) {
chrome.storage.local.clear();
}
});
But apparently this will not only clear the local storage when I reload the page but when something change in it causing a lot of buggy behavior on the popup (basically changing very randomly the text of the button when the user interact/click on it everytime they open the popup). My question is which is the right way to clear the local storage just when the page is reloaded / refreshed. I'm sorry if this seems pretty obvious but I'm new on the Chrome Extension development world.

Why does the code inside xmlhttprequest work when async flag is set to false but not when it is set to true

I am fairly new to the use of javascript and would appreciate any help.
I have an application where the browser must use xmlhttprequest to receive a response from the server (true/false for testing purposes) and based on the response, the client will open a file selection dialog for the user to select a local file for uploading.
When I create the XMLHttpRequest with the async flag set to FALSE, when the client receives a "true" response from the server, a file selection dialog box opens (for both Chrome, IE).
When I create the XMLHttpRequest with the async flag set to TRUE ( as recommended), when the client receives a "true" response from the server, the same code path is followed, however a file selection dialog box never opens and no errors are displayed in the debuggers for Chrome, HOWEVER it still work in IE.
Here is the code:
...
// user has clicked button to upload a file
$scope.uploadFile = function () {
request = new XMLHttpRequest();
request.open("GET", "some-url", true);// false
request.onreadystatechange = $scope.checkUploadPermissions;
request.send();
}
// the callback
// If the user has permission (based on various factors not shown here)
// we open the dialog
// Otherwise we inform them that they are not allowed
$scope.checkUploadPermissions = function () {
// simplified for brevity
if (request.readyState == 4 && request.status == 200) {
// for this sample, we are only checking if the server returned true/false
var hasPerms = request.responseText;
if (hasPerms === "true") {
$scope.openFileSelector();
}
else {
alert("You do not have permission to upload a file.");
}
}
}
// if the user has permission to proceed, we trigger a click on a hidden element
$scope.openFileSelector = function () {
angular.element("#presentUpload").trigger("click");
}
...
I would like to reiterate that this code works perfectly when the async flag set to FALSE but not when it is set to TRUE.
How can I have this work properly when setting the flag to TRUE.
Thank you in advance.
File upload is a feature in a browser that can only be initiated as the direct result of a user action (usually while your JS code is processing a mouse click or keyboard event). It cannot be initiated asynchronously by a timer or via some async callback.
So, when you set your first Ajax call to sync, then your JS click on a hidden element appears to the browser as it is still inside the hidden element click event and thus the upload is allowed. When your first Ajax call is set to async, the user click event is over by the time you try to click the hidden element and the browser will not bring up the upload dialog.
See Trigger click on input=file on asynchronous ajax done() for details.

Categories