Reputation: 6761
I'm trying to write a custom handler for the link input value. In case the user inputs a link that does not have a custom protocol, I wish to prepend a http:
before the input value. That's because if link value lacks http:, link is not interpreted and about:blank
is shown intead. (https://github.com/quilljs/quill/issues/1268#issuecomment-272959998)
Here's what I've written (similar to the official example here):
toolbar.addHandler("link", function sanitizeLinkInput(linkValueInput) {
console.log(linkValueInput); // debugging
if (linkValueInput === "")
this.quill.format(false);
// do nothing, since it implies user has just clicked the icon
// for link, hasn't entered url yet
else if (linkValueInput == true);
// do nothing, since this implies user's already using a custom protocol
else if (/^\w+:/.test(linkValueInput));
else if (!/^https?:/.test(linkValueInput)) {
linkValueInput = "http:" + linkValueInput;
this.quill.format("link", linkValueInput);
}
});
Every time the user clicks the link icon, nothing happens and true
is logged to the console. I actually wished this handler to be executed when person clicks "save" on the tooltip that's shown after pressing the link icon.
Any idea how to do this? Hints or suggestions are also appreciated.
Thanks!
Upvotes: 11
Views: 19376
Reputation: 10164
Maybe this is an old question but this is the way I make it work.
First, it whitelist other custom protocols to be accepted as valid ones.
Then, we run the sanitize
method that is already included on the Quill core, and based on the custom protocols list it will return the URL or about:blank
.
Then, if this is a about:blank
is because it did not pass the sanitization method. If we get the URL then we verify whether or not it has a protocol from the list and if not, then we append http://
and in that way we do not get relative URL or blank unless it is not being whitelisted:
https://your-site.com/www.apple.com
about:blank
Hope it helps anyone else having this same issue.
const Link = Quill.import('formats/link')
// Override the existing property on the Quill global object and add custom protocols
Link.PROTOCOL_WHITELIST = ['http', 'https', 'mailto', 'tel', 'radar', 'rdar', 'smb', 'sms']
class CustomLinkSanitizer extends Link {
static sanitize(url) {
// Run default sanitize method from Quill
const sanitizedUrl = super.sanitize(url)
// Not whitelisted URL based on protocol so, let's return `blank`
if (!sanitizedUrl || sanitizedUrl === 'about:blank') return sanitizedUrl
// Verify if the URL already have a whitelisted protocol
const hasWhitelistedProtocol = this.PROTOCOL_WHITELIST.some(function(protocol) {
return sanitizedUrl.startsWith(protocol)
})
if (hasWhitelistedProtocol) return sanitizedUrl
// if not, then append only 'http' to not to be a relative URL
return `http://${sanitizedUrl}`
}
}
Quill.register(CustomLinkSanitizer, true)
Upvotes: 0
Reputation: 153
I have to do same exact thing,(validate url before sending to server) so I end up with some thing like this.
const editor = new DOMParser().parseFromString(value,
'text/html');
const body = qlEditor.getElementsByTagName('body');
const data = document.createElement('div');
data.innerHTML = body[0].innerHTML;
Array.from(data.querySelectorAll('a')).forEach((ele) => {
let href = ele.getAttribute('href');
if (!href.includes('http') && !href.includes('https')) {
href = `https://${href}`;
ele.setAttribute('href', href);
}
});
body[0].innerHTML = data.innerHTML;
Upvotes: 0
Reputation: 2404
after spending half an hour
found this solution
htmlEditorModuleConfig = {
toolbar: [
['link']
],
bounds: document.body
}
Add 'bounds: document.body' in configuration
Upvotes: 0
Reputation: 6761
congregating all the information
The snow theme itself uses the toolbar's addHandler to show a tooltip and so it is impossible to use the addHandler method to achieve what we wish to.
So, instead we can do the following:
var Link = Quill.import('formats/link');
var builtInFunc = Link.sanitize;
Link.sanitize = function customSanitizeLinkInput(linkValueInput) {
var val = linkValueInput;
// do nothing, since this implies user's already using a custom protocol
if (/^\w+:/.test(val));
else if (!/^https?:/.test(val))
val = "http:" + val;
return builtInFunc.call(this, val); // retain the built-in logic
};
this method doesn't hook onto handlers but instead modifies the built-in sanitisation logic itself. We have also retained the original behavior of the sanitisation so that doesn't modify the editor's original behavior.
Alternatively, we could actually hook onto the save button of the tooltip, using this code. But it is too long a method compared to the one above.
Upvotes: 16
Reputation: 14767
The toolbar handler just calls your given function when the button in the toolbar is clicked. The value
passed in depends on the status of that format in the user's selection. So if the user has highlighted just plain text, and clicked the link button, you will get false
. If the user highlighted the link, you will get the value of the link, which is by default the url. This is explained with an example here: http://quilljs.com/docs/modules/toolbar/#handlers.
The snow theme uses toolbar's addHandler itself to show a tooltip and it looks like you are trying to hook into this, which is not possible at the moment.
It looks like what you are really trying to do is control the sanitization logic of a link. Sanitization exists at a lower level in Quill since there are many ways to insert a link, for example from the tooltip UI, from paste, from the different APIs, and more. So to cover them all the logic is in the link format itself. An example of custom formats of links specifically is covered in http://quilljs.com/guides/cloning-medium-with-parchment/#links. You can also just modify Quill's own sanitize method but this is not recommended as it is not documented nor covered by semver.
let Link = Quill.import('formats/link');
Link.sanitize = function(value) {
return 'customsanitizedvalue';
}
Upvotes: 2
Reputation: 11050
As far as I can tell, the handling of creating and updating links is a bit distributed in Quill's sources. The default Snow theme handles editing links to some extent: it tracks the user selection and last selected link internally. Because of this I do not think that it is possible to achieve what you want currently in Quill using only a custom handler.
You may want to open an issue to report this, the authors might be willing to add such a handler.
In the meantime I came up with a way to update the link by simply listening for events causing the edit tooltip to close. There are some complications, because a link can be edited and the theme then relies on its internal tracking to update it. However, all in all I think that this solution is not too bad. You might want to add some error checking here and there, but overall it seems to work nicely and do what you want it do to. I have created a Fiddle demonstrating this. For completeness, I have included it here as a code snippet too.
var quill = new Quill('#editor', {
modules: {
toolbar: true
},
theme: 'snow'
}),
editor = document.getElementById('editor'),
lastLinkRange = null;
/**
* Add protocol to link if it is missing. Considers the current selection in Quill.
*/
function updateLink() {
var selection = quill.getSelection(),
selectionChanged = false;
if (selection === null) {
var tooltip = quill.theme.tooltip;
if (tooltip.hasOwnProperty('linkRange')) {
// user started to edit a link
lastLinkRange = tooltip.linkRange;
return;
} else {
// user finished editing a link
var format = quill.getFormat(lastLinkRange),
link = format.link;
quill.setSelection(lastLinkRange.index, lastLinkRange.length, 'silent');
selectionChanged = true;
}
} else {
var format = quill.getFormat();
if (!format.hasOwnProperty('link')) {
return; // not a link after all
}
var link = format.link;
}
// add protocol if not there yet
if (!/^https?:/.test(link)) {
link = 'http:' + link;
quill.format('link', link);
// reset selection if we changed it
if (selectionChanged) {
if (selection === null) {
quill.setSelection(selection, 0, 'silent');
} else {
quill.setSelection(selection.index, selection.length, 'silent');
}
}
}
}
// listen for clicking 'save' button
editor.addEventListener('click', function(event) {
// only respond to clicks on link save action
if (event.target === editor.querySelector('.ql-tooltip[data-mode="link"] .ql-action')) {
updateLink();
}
});
// listen for 'enter' button to save URL
editor.addEventListener('keydown', function(event) {
// only respond to clicks on link save action
var key = (event.which || event.keyCode);
if (key === 13 && event.target === editor.querySelector('.ql-tooltip[data-mode="link"] input')) {
updateLink();
}
});
<link href="https://cdn.quilljs.com/1.1.10/quill.snow.css" rel="stylesheet" />
<script src="https://cdn.quilljs.com/1.1.10/quill.min.js"></script>
<div id="editor"></div>
Let me know if you have any questions.
Upvotes: 4