I am looking for a way to disable the popup message which shows when using window.showDirectoryPicker. This page is served from localhost via WebView2 component in a WPF app.
<!DOCTYPE html>
<html>
<body>
<div class="fl" style="margin: 0 0 2rem 0"><button id="addToFolder">Give access to folder</button></div>
</body>
</html>
<script>
let directory;
document.getElementById('addToFolder').addEventListener('click', async () => {
try {
directory = await window.showDirectoryPicker({
startIn: 'desktop'
});
for await (const entry of directory.values()) {
let newEl = document.createElement('div');
newEl.innerHTML = `<strong>${entry.name}</strong> - ${entry.kind}`;
document.getElementById('folder-info').append(newEl);
}
} catch (e) {
console.log(e);
}
});
</script>
Note that I already have full file system access via the WPF app, so its not a security concern.
Related
I am trying to render some HTML code in flutter web which takes a dynamic value of sessionID and that's the reason I cannot have the html file as a asset. I've also tried a lot of packages but they are not working in web, in mobile its working fine.
I'm basically implementing Mastercard Hosted Checkout but it's not getting rendered directly in flutter web.
I'd tried IFrameElement but there's some problem with it, it immediately breaks off and the code doesn't works in flutter web. Maybe because its being handled in initState. Anyway here's the code of IframeElement please let me know if there's some way to deal with it.
late html.IFrameElement _element;
void initState() {
super.initState();
createSession().then((sessionId) {
setState(() {
_sessionId = sessionId;
_sessionId = _sessionId;
});
}).catchError((error) {
debugPrint(error.toString());
});
_element = html.IFrameElement()
..style.border = 'none'
..srcdoc = """
<html>
<head>
<script src="https://test-network.mtf.gateway.mastercard.com/static/checkout/checkout.min.js" data-error="errorCallback" data-cancel="cancelCallback"></script>
<script type="text/javascript">
function errorCallback(error) {
console.log(JSON.stringify(error));
}
function cancelCallback() {
console.log('Payment cancelled');
}
Checkout.configure({
session: {
id: '$_sessionId'
}
});
</script>
</head>
<body>
...
<div id="embed-target"> </div>
<input type="button" value="Pay with Embedded Page" onclick="Checkout.showEmbeddedPage('#embed-target');" />
<input type="button" value="Pay with Payment Page" onclick="Checkout.showPaymentPage();" />
...
</body>
</html>
""";
// ignore:undefined_prefixed_name
ui.platformViewRegistry.registerViewFactory(
'example',
(int viewId) => _element,
);
}
I have my index.html and the necessary .js files on heroku. Everything works fine. Now, I don't want to send my users to "myappname.herokuapp.com", so I plan to use my own website to store the .html file, but when the user taps "submit" on my HTML form, I want to execute the Herok NodeJS code.
Here is what the html looks like
<script>
const form = document.querySelector("form");
form.addEventListener("submit", async (e) => {
e.preventDefault();
try {
displayStatus("processing...");
const request = new Request("/file-upload", {
method: "POST",
body: new FormData(form),
});
const res = await fetch(request);
const resJson = await res.json();
displayResult(resJson.result);
} catch (err) {
displayStatus("an unexpected error");
console.error(err);
}
});
function displayResult(result) {
const content = `Your ID: ${result.id}`;
displayStatus(content);
}
function displayStatus(msg) {
result.textContent = msg;
}
</script>
How can I call this "/file-upload" from my HTML that is located on "mywebsite.com/index.html" while the actual NodeJS logic runs on "myappname.herokuapp.com"
I've tried to replace the "/file-upload" with "myappname.herokuapp.com/file-upload" but it doesn't work.
Again, the goal is to use what I have on Heroku, but not have the users go to "myappname.herokuapp.com" instead they should go to "mywebsite.com/index.html"
Thank you
Actually, replacing "/file-upload" with "myappname.herokuapp.com/file-upload" did the trick. The only issue is my "const request = new Request" request returning an error all the time, but Heroku logs shows a successful execution of "file-upload"
My Html code has Button-tags that have same id "hoge".
If you get the selector from the Chrome Dev Tool, it will be the same for both "#hoge".
<html>
<body>
<button id="hoge">Hoge</button>
<div class="shadow">
#shadow-root (open)
<button id="hoge">Hoge</button>
</div>
</body>
</html>
I want to get element of button-tag in shadow dom with puppeteer.
But, my javascript code gets element of 1st button.
const element = page.waitForSelector("pierce/#hoge");
This is not what I want.
I'm guessing it's because you didn't specify a unique selector, but i don't know what is unique selector for puppeteer.
If you know how to solve this problem, please let me know.
Long story short
I work with puppeteer a lot and wanted this knowlegde to be in my bag. One way to select a shadow Element is by accessing the parent DOM Node's shadowRoot property. The answer is based on this article.
Accessing Shadow Root property
For your html example this does the trick:
const button = document.querySelector('.shadow').shadowRoot.querySelector('#hoge')
waiting
Waiting though is a little more complicated but can be acquired using page.waitForFunction().
Working Sandbox
I wrote this full working sandbox example on how to wait for a certain shadowRoot element.
index.html (located in same directory as app.js)
<html>
<head>
<script>
// attach shadowRoot after 6 seconds for emulating waiting..
setTimeout(() => {
const btn = document.getElementById('hoge')
const container = document.getElementsByClassName('shadow')[0]
const shadowRoot = container.attachShadow({
mode: 'open'
})
shadowRoot.innerHTML = `<button id="hoge" onClick="doStuff()">hoge2</button>`
console.log('attached!.')
}, 6000)
function doStuff() {
alert('shadow button clicked!')
}
</script>
</head>
<body>
<button id="hoge">Hoge</button>
<div class="shadow">
</div>
</body>
</html>
app.js (located in same directory as index.html)
var express = require('express')
var { join } = require('path')
var puppeteer = require('puppeteer')
//utility..
const wait = (seconds) => {
console.log('waiting', seconds, 'seconds')
return new Promise((res, rej) => {
setTimeout(res, seconds * 1000)
})
}
const runPuppeteer = async() => {
const browser = await puppeteer.launch({
defaultViewport: null,
headless: false
})
const page = await browser.newPage()
await page.goto('http://127.0.0.1:5000')
await wait(3)
console.log('page opened..')
// only execute this function within a page context!.
// for example in page.evaluate() OR page.waitForFunction etc.
// don't forget to pass the selector args to the page context function!
const selectShadowElement = (containerSelector, elementSelector) => {
try {
// get the container
const container = document.querySelector(containerSelector)
// Here's the important part, select the shadow by the parentnode of the
// actual shadow root and search within the shadowroot which is like another DOM!,
return container.shadowRoot.querySelector(elementSelector)
} catch (err) {
return null
}
}
console.log('waiting for shadow elemetn now.')
const containerSelector = '.shadow'
const elementSelector = '#hoge'
const result = await page.waitForFunction(selectShadowElement, { timeout: 15 * 1000 }, containerSelector, elementSelector)
if (!result) {
console.error('Shadow element not found..')
return
}
// since waiting succeeded we can get the elemtn now.
const element = await page.evaluateHandle(selectShadowElement, containerSelector, elementSelector)
try {
// click the element.
await element.click()
console.log('clicked')
} catch (err) {
console.log('failed to click..')
}
await wait(10)
}
var app = express()
app.get('/', (req, res) => {
res.sendFile(join(__dirname, 'index.html'))
})
app.listen(5000, '127.0.0.1', () => {
console.log('listening!')
runPuppeteer()
})
Start example
$ npm i express puppeteer
$ node app.js
Make sure to use headless:false option to see what's happening.
The application does this:
start a small express server only serving index.html on /
open puppeteer after server has started and wait for the shadow root element to appear.
Once it appeared, it gets clicked and an alert() is shown. => success!
Browser Support
Tested with chrome.
Cheers ' ^^
I am trying to explore on web NFC and found a simple sample (https://googlechrome.github.io/samples/web-nfc/). So, I copy the sample code to test it on local:
<html><head>
<title>Web NFC Sample</title>
<script>
// Add a global error event listener early on in the page load, to help ensure that browsers
// which don't support specific functionality still end up displaying a meaningful message.
window.addEventListener('error', function(error) {
if (ChromeSamples && ChromeSamples.setStatus) {
console.error(error);
ChromeSamples.setStatus(error.message + ' (Your browser may not support this feature.)');
error.preventDefault();
}
});
</script>
<link rel="stylesheet" href="Web%20NFC%20Sample_files/main.css">
</head>
<body>
<button id="scanButton">Scan</button>
<button id="writeButton">Write</button>
<script>
var ChromeSamples = {
log: function() {
var line = Array.prototype.slice.call(arguments).map(function(argument) {
return typeof argument === 'string' ? argument : JSON.stringify(argument);
}).join(' ');
document.querySelector('#log').textContent += line + '\n';
},
clearLog: function() {
document.querySelector('#log').textContent = '';
},
setStatus: function(status) {
document.querySelector('#status').textContent = status;
},
setContent: function(newContent) {
var content = document.querySelector('#content');
while(content.hasChildNodes()) {
content.removeChild(content.lastChild);
}
content.appendChild(newContent);
}
};
</script>
<h3>Live Output</h3>
<div id="output" class="output">
<div id="content"></div>
<div id="status">Web NFC is not available.
Please make sure the "Experimental Web Platform features" flag is enabled on Android.</div>
<pre id="log"></pre>
</div>
<script>
if (/Chrome\/(\d+\.\d+.\d+.\d+)/.test(navigator.userAgent)){
// Let's log a warning if the sample is not supposed to execute on this
// version of Chrome.
if (89 > parseInt(RegExp.$1)) {
ChromeSamples.setStatus('Warning! Keep in mind this sample has been tested with Chrome ' + 89 + '.');
}
}
</script>
<script>
log = ChromeSamples.log;
if (!("NDEFReader" in window))
ChromeSamples.setStatus(
"Web NFC is not available.\n" +
'Please make sure the "Experimental Web Platform features" flag is enabled on Android.'
);
</script>
<script>scanButton.addEventListener("click", async () => {
log("User clicked scan button");
try {
const ndef = new NDEFReader();
await ndef.scan();
log("> Scan started");
ndef.addEventListener("readingerror", () => {
log("Argh! Cannot read data from the NFC tag. Try another one?");
});
ndef.addEventListener("reading", ({ message, serialNumber }) => {
log(`> Serial Number: ${serialNumber}`);
log(`> Records: (${message.records.length})`);
});
} catch (error) {
log("Argh! " + error);
}
});
writeButton.addEventListener("click", async () => {
log("User clicked write button");
try {
const ndef = new NDEFReader();
await ndef.write("Hello world!");
log("> Message written");
} catch (error) {
log("Argh! " + error);
}
});
</script>
</body></html>
But when I run it, it shows Web NFC is not available. Please make sure the "Experimental Web Platform features" flag is enabled on Android. on the message. When I click on "scan" button, it shows Argh! ReferenceError: NDEFReader is not defined.
May I know why the sample code work well when it is on https://googlechrome.github.io but can't work when I have it on my local PC? Thank you.
As documented in https://web.dev/nfc/#security-and-permissions, Web NFC is only available in secure browsing contexts. It means you either have to serve your webpage over https:// or localhost such as http://127.0.0.1 or http://localhost.
If you have installed npm, you can use npx http-serve.
If you have installed Python 2, use python -m SimpleHTTPServer
If you have installed Python 3, use python -m http.server
I have tried Web Serial API.
If I use requestPort() method, like this :
<button id="connect">Connect</button>
<script>
const connectButton = document.getElementById("connect");
connectButton.addEventListener('click', async () => {
try {
const port = await navigator.serial.requestPort();
console.log(port)
} catch (e) {
console.error(e)
}
});
</script>
It's OK, to prompt the user for which device the site should be allowed to control.
But with getPorts() method, like this :
<script>
const connectButton = document.getElementById("connect");
connectButton.addEventListener('click', async () => {
try {
const ports = await navigator.serial.getPorts();
console.log(ports)
} catch (e) {
console.error(e)
}
});
</script>
I got nothing with ports.length == 0
Could anybody help this?
Thanks
A user must approve access to the device before it can be accessed. You initiate the request via:
navigator.serial.requestPort()
To get a list of ports that have already been selected/approved by the user from the permission popup you call:
navigator.serial.getPorts()
You can check out the https://webserial.io source for an example here:
https://github.com/williamkapke/webserial/blob/main/src/stores/connection.js#L41