programmatically use w3c css/html validators with custom html - javascript

edit
I've got it working for CSS, only HTML validation to go.
let css = Array.from(document.styleSheets)
.reduce((combinedsheet,sheet) => sheet.rules?
combinedsheet + Array.from(sheet.rules)
.reduce((p,c) => p+c.cssText+'\n', ''):
combinedsheet, '')
try {
document.querySelector('a.validation.css').href =
'https://jigsaw.w3.org/css-validator/validator?text=' +
encodeURIComponent(css) +
'&profile=css3&usermedium=all&warning=1&vextwarning='
document.querySelector('a.validation.html').href =
'https://validator.w3.org/nu/?doc=' +
encodeURIComponent(document.querySelector('html'))
} catch (e) {
// this will fail before page fully loads, and we can be silent about that
}
edit #2
I've got it working. The only problem is that this uses a "popup" instead of opening a window silently like target="blank" would. I'm using an onlick method:
get_html_validation: function () {
let fd = new FormData()
fd.append('fragment', '<!DOCTYPE html>' +
document.querySelector('html').outerHTML)
fd.append('prefill', 0)
fd.append('doctype', 'Inline')
fd.append('prefill_doctype', 'html401')
fd.append('group', 0)
axios.post("https://validator.w3.org/nu/#textarea", fd)
.then(response => {
let win=window.open('about:blank')
console.log(win)
with(win.document)
{
open()
write(response.data)
close()
}
})
.catch(e => console.error(e))
}
original
I'd like to use these validators programmatically:
https://validator.w3.org/nu
http://jigsaw.w3.org/css-validator/validator
— they work just fine if you pass a URL as a parameter, so long as the document doesn't have javascript that manipulates the dom. But... mine does.
How can I hand these sites custom html to check using javascript in the browser? I'm looking to do something like:
onValidateDOM = () => {
let toValidate = '<!DOCTYPE html>' + document.querySelector('html').outerHTML
let returnURLs = []
returnURLs.push(w3cCSS.validate(toValidate), w3cHTML.validate(toValidate)
return returnURLs
}

Set toValidate passed to encodeURIComponent() as value to doc= query
fetch(`https://validator.w3.org/nu/?doc=${encodeURIComponent(toValidate)}`)
.then(response => response.text())
.then(text => console.log(text));

Related

Javascript working in Chrome, working in firefox locally, but not after deployment

This is part of a Spark Java app, but the error is happening in this JS part. The relevant code is as follows:
const addErrBox = async () => {
const controls = document.querySelector(".pure-controls");
const errBox = document.createElement("p");
errBox.setAttribute("id", "errBox");
errBox.setAttribute("style", "color:red");
errBox.innerHTML = "Short URL not valid or already in use!";
controls.appendChild(errBox);
}
const submitForm = () => {
const form = document.forms.namedItem("new-url-form");
const longUrl = form.elements["longUrl"];
const shortUrl = form.elements["shortUrl"];
const url = `/api/new`;
fetch(url, {
method: "POST",
body: `${longUrl.value};${shortUrl.value}`
})
.then((res) => {
if (!res.ok) {
if (document.getElementById("errBox") == null) {
addErrBox();
}
}
else {
document.getElementById("errBox")?.remove();
longUrl.value = "";
shortUrl.value = "";
refreshData();
}
});
};
(async () => {
await refreshData();
const form = document.forms.namedItem("new-url-form");
form.onsubmit = e => {
e.preventDefault();
submitForm();
}
})();
Basically, "/api/new" checks for validity of input, adds the data to database if valid and prints error otherwise. Now, when the input is valid, it seems to work. The "/api/new" code is in Java, which seems to work properly as well since I do get a 400 error. All of it works properly when built inside a docker locally, but when accessed over internet using Nginx reverse proxy, it stops working inside firefox. Chrome still works. I'm not sure what's happening.
The code for "/api/new" is this:
public static String addUrl(Request req, Response res) {
var body = req.body();
if (body.endsWith(";")) {
body = body + "$";
}
var split = body.split(";");
String longUrl = split[0];
if (split[1].equals("$")) {
split[1] = Utils.randomString();
}
String shortUrl = split[1];
shortUrl = shortUrl.toLowerCase();
var shortUrlPresent = urlRepository
.findForShortUrl(shortUrl);
if (shortUrlPresent.isEmpty() && Utils.validate(shortUrl)) {
return urlRepository.addUrl(longUrl, shortUrl);
} else {
res.status(HttpStatus.BAD_REQUEST_400);
return "shortUrl not valid or already in use";
}
}
Update: it suddenly started working, without any change on the server side. I think it was some kind of issue with caching, either in firefox, cloudflare or Nginx's part.

Async JS validation issues for html textarea

I'm trying to replicate the code in this article:
https://depth-first.com/articles/2020/08/24/smiles-validation-in-the-browser/
What I'm trying to do different is that I'm using a textarea instead of input to take multi-line input. In addition to displaying an error message, I also want to display the entry which doesn't pass the validation.
The original validation script is this:
const path = '/target/wasm32-unknown-unknown/release/smival.wasm';
const read_smiles = instance => {
return smiles => {
const encoder = new TextEncoder();
const encoded = encoder.encode(`${smiles}\0`);
const length = encoded.length;
const pString = instance.exports.alloc(length);
const view = new Uint8Array(
instance.exports.memory.buffer, pString, length
);
view.set(encoded);
return instance.exports.read_smiles(pString);
};
};
const watch = instance => {
const read = read_smiles(instance);
document.querySelector('input').addEventListener('input', e => {
const { target } = e;
if (read(target.value) === 0) {
target.classList.remove('invalid');
} else {
target.classList.add('invalid');
}
});
}
(async () => {
const response = await fetch(path);
const bytes = await response.arrayBuffer();
const wasm = await WebAssembly.instantiate(bytes, { });
watch(wasm.instance);
})();
For working with a textarea, I've changed the watch function to this and added a <p id="indicator"> element to the html to display an error:
const watch = instance => {
const read = read_smiles(instance);
document.querySelector("textarea").addEventListener('input', e => {
const { target } = e;
var lines_array = target.value.split('/n');
var p = document.getElementById("indicator");
p.style.display = "block";
p.innerHTML = "The size of the input is : " + lines_array.length;
if (read(target.value) === 0) {
target.classList.remove('invalid');
} else {
target.classList.add('invalid');
}
});
}
I'm not even able to get a count of entries that fail the validation. I believe this is async js and I'm just a beginner in JavaScript so it's hard to follow what is happening here, especially the part where the function e is referencing itself.
document.querySelector("textarea").addEventListener('input', e => {
const { target } = e;
Can someone please help me in understanding this complicated code and figuring out how to get a count of entries that fail the validation and also printing the string/index of the same for helping the user?
There is a mistake in you code to count entries in the textarea:
var lines_array = target.value.split('\n'); // replace /n with \n
You are asking about the function e is referencing itself:
The destructuring assignment syntax is a JavaScript expression that makes it possible to unpack values from arrays, or properties from objects, into distinct variables. You can find more informations Mdn web docs - Destructuring object

How to wait for a promise inside an each loop?

New to promises - trying to work out the logic.
I'm trying to grab youtube preview thumbnails automatically like this answer advises:
const youtubeAnimation = id => {
return fetch(
`https://cors-anywhere.herokuapp.com/https://www.youtube.com/results?search_query=${id}&sp=QgIIAQ%253D%253D`,
{ headers: {} }
)
.then(r => r.text())
.then(html => {
const found = html.match(
new RegExp(`["|']https://i.ytimg.com/an_webp/${id}/(.*?)["|']`)
);
if (found) {
try {
const url = JSON.parse(found[0]);
return url;
} catch (e) {}
}
throw "not found";
})
.catch(e => {
return false;
});
};
I store the youtube ID in the video attribute's container, and then do this which works well for a single video on the page:
youtubeAnimation($('.video').attr('youtube')).then(function (value) {
$('<img class="videopreview" src="' + value + '">').insertBefore($('.video'));
});
But I need to be able to run this with a variable number of $('.video') elements on the page. I've tried putting it in an each loop:
$(".video").each(function () {
youtubeAnimation($(this).attr('youtube')).then(function (value) {
$('<img class="videopreview" src="' + value + '">').insertBefore($(this));
});
});
But all this does is add the preview thumbnail to the last $('.video') element. When testing with console.log I can see that all preview images are being discovered, but that only the last image is applied to the last element. I need to somehow pause the each loop until youtubeAnimation does its thing, before proceeding to the next element.
What are the additional steps required to make this all work? And also, what is the reason that answer gave youtubeAnimation as a const, and not a function?
One option that I would try is:
$(".video").each(async () => {
const animation = await youtubeAnimation($(this).attr('youtube'));
$('<img class="videopreview" src="' + value + '">').insertBefore($(this));
});
It utilizes arrow function to preserve this and async/await to make the code more readable.

Changing the source of image tag in Electron app

I have an <img> tag that represents the album cover of songs. I can fetch the song's album cover picture using music-metadata parser, like this:
const mm = require('music-metadata')
mm.parseFile(music_filepath)
.then(metadata => {
console.log(metadata)
})
.catch(error => {
console.error(error.message)
})
Now if I look at the console, I have something like this:
So I can fetch the picture using metadata['common']['picture'][0]['data'].
The HTML for the image tag is this (which uses a default picture for album cover):
<img id="album-cover" src="assets/images/Album Cover.jpg" width="200px" height="200px">
I'm aware that I can change this picture using pure JavaScript, like this:
let album_cover = document.getElementById('album-cover')
album_cover.src = new_picture_path
But this method uses file path. How can I change the picture directly from metadata?
You can generate a base64 of your image and pass it to the attribute of your image by doing something like this :
const request = require('request').defaults({ encoding: null });
const mm = require('music-metadata')
const generateBase64 = url => {
return new Promise(
resolve => {
request.get(url, (error, response, body) => {
if (!error && response.statusCode == 200) {
const data = "data:" + response.headers["content-type"] + ";base64," + new Buffer.from(body).toString('base64')
return (resolve(data))
}
return(resolve(null))
}
)}
)
}
mm.parseFile(music_filepath)
.then(async metadata => {
const base64 = await generateBase64(metadata)
if (!base64) return;
const album_cover = document.getElementById('album-cover')
album_cover.setAttribute('src', base64);
})
.catch(error => {
console.error(error.message)
})
you can set base64 ed img
document.getElementById('imgTest')
.setAttribute('src', 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAU//8/==');
Thanks to the answer provided by aperesso, I solved it like this.
let acf = metadata['common']['picture'][0]['format'] // album_cover_format
let acd = metadata['common']['picture'][0]['data'] // album_cover_data
album_cover.src = `data:${acf};base64,${Buffer.from(acd).toString('base64')}`

Appending property to ArrayBuffer sent over DataChannel

I am currently receiving chunks from a stream of a video that I send over the DataChannel to a peer who will then reconstruct the video on the other end.
I have this part working just fine but I would like to add which chunk # it was that was received so that it doesn't matter if they happen to arrive in a different order than intended.
Initially I thought that adding a parameter chunkId would work but when I do .data.chunkId on the receiver side, it is undefined.
Then I tried to stringify the ArrayBuffer along with the chunkId using JSON.stringify({ "chunkId": chunkId, "data": chunk }) but it causes problems when I parse it on the other end (Unexpected end of JSON input and Unexpected token , in JSON at position #)
DataChannels also accept blobs so I thought I would try that but the sender is using node.js which apparently can't do that. I wasn't quite able to figure out how to get around that.
The last thing I tried was to simply append the chunkId to the beginning/end of the ArrayBuffer itself but when I try to create a new array I get the error source is too large when trying to add the chunk itself.
What is the correct way of achieving this?
You should be able to intermingle sending text and ArrayBuffers, and check for them on reception:
var pc1 = new RTCPeerConnection(), pc2 = new RTCPeerConnection();
pc1.onicecandidate = e => pc2.addIceCandidate(e.candidate);
pc2.onicecandidate = e => pc1.addIceCandidate(e.candidate);
pc1.oniceconnectionstatechange = e => log(pc1.iceConnectionState);
pc1.onnegotiationneeded = e =>
pc1.createOffer().then(d => pc1.setLocalDescription(d))
.then(() => pc2.setRemoteDescription(pc1.localDescription))
.then(() => pc2.createAnswer()).then(d => pc2.setLocalDescription(d))
.then(() => pc1.setRemoteDescription(pc2.localDescription))
.catch(e => log(e));
var dc1 = pc1.createDataChannel("chat", {negotiated: true, id: 0});
var dc2 = pc2.createDataChannel("chat", {negotiated: true, id: 0});
dc2.binaryType = "arraybuffer";
dc2.onmessage = e => {
if (e.data instanceof ArrayBuffer) {
log("Got ArrayBuffer!");
} else if (e.data instanceof Blob) {
log("Got Blob!");
} else {
log("> " + e.data);
}
}
button.onclick = e => dc1.send(new ArrayBuffer(8));
chat.onkeypress = e => {
if (e.keyCode != 13) return;
dc1.send(chat.value);
chat.value = "";
};
var log = msg => div.innerHTML += "<br>" + msg;
Chat: <input id="chat"><button id="button">Send ArrayBuffer</button><br>
<div id="div"></div>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
So why not send the chunk id ahead of each ArrayBuffer?

Categories

Resources