forms: detect and suggest fixes for typos in email domains (#42396)
This commit is contained in:
parent
a8f1e4132f
commit
417c7f6910
|
@ -875,6 +875,9 @@ class EmailWidget(StringWidget):
|
|||
if not 'size' in kwargs:
|
||||
self.attrs['size'] = '35'
|
||||
|
||||
def add_media(self):
|
||||
get_response().add_javascript(['jquery.js', '../../i18n.js', 'qommon.forms.js'])
|
||||
|
||||
def _parse(self, request):
|
||||
StringWidget._parse(self, request)
|
||||
if self.value is not None:
|
||||
|
|
|
@ -1916,3 +1916,39 @@ div.mail-body {
|
|||
#sidebar-custom-views .active {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.field-live-hint {
|
||||
position: absolute;
|
||||
background: #ffffee;
|
||||
color: #333;
|
||||
z-index: 1000000;
|
||||
padding: 1em 1em;
|
||||
box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.16);
|
||||
}
|
||||
|
||||
.field-live-hint span::before {
|
||||
font-family: FontAwesome;
|
||||
content: "\f071"; /* exclamation triangle */
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
|
||||
.field-live-hint button.action,
|
||||
.field-live-hint button.close {
|
||||
margin: 0 1em;
|
||||
padding: 0;
|
||||
color: blue !important;
|
||||
border: none;
|
||||
text-decoration: underline !important;
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.field-live-hint button.close {
|
||||
color: #333 !important;
|
||||
margin: 0;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.field-live-hint button.close::after {
|
||||
content: "×";
|
||||
}
|
||||
|
|
|
@ -1,3 +1,80 @@
|
|||
String.prototype.similarity = function(string) {
|
||||
// adapted from https://github.com/jordanthomas/jaro-winkler (licensed as MIT)
|
||||
var s1 = this, s2 = string;
|
||||
var m = 0;
|
||||
var i;
|
||||
var j;
|
||||
|
||||
// Exit early if either are empty.
|
||||
if (s1.length === 0 || s2.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Convert to upper
|
||||
s1 = s1.toUpperCase();
|
||||
s2 = s2.toUpperCase();
|
||||
|
||||
// Exit early if they're an exact match.
|
||||
if (s1 === s2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
var range = (Math.floor(Math.max(s1.length, s2.length) / 2)) - 1;
|
||||
var s1Matches = new Array(s1.length);
|
||||
var s2Matches = new Array(s2.length);
|
||||
|
||||
for (i = 0; i < s1.length; i++) {
|
||||
var low = (i >= range) ? i - range : 0;
|
||||
var high = (i + range <= (s2.length - 1)) ? (i + range) : (s2.length - 1);
|
||||
|
||||
for (j = low; j <= high; j++) {
|
||||
if (s1Matches[i] !== true && s2Matches[j] !== true && s1[i] === s2[j]) {
|
||||
++m;
|
||||
s1Matches[i] = s2Matches[j] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exit early if no matches were found.
|
||||
if (m === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Count the transpositions.
|
||||
var k = 0;
|
||||
var numTrans = 0;
|
||||
|
||||
for (i = 0; i < s1.length; i++) {
|
||||
if (s1Matches[i] === true) {
|
||||
for (j = k; j < s2.length; j++) {
|
||||
if (s2Matches[j] === true) {
|
||||
k = j + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (s1[i] !== s2[j]) {
|
||||
++numTrans;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var weight = (m / s1.length + m / s2.length + (m - (numTrans / 2)) / m) / 3;
|
||||
var l = 0;
|
||||
var p = 0.1;
|
||||
|
||||
if (weight > 0.7) {
|
||||
while (s1[l] === s2[l] && l < 4) {
|
||||
++l;
|
||||
}
|
||||
|
||||
weight = weight + l * p * (1 - weight);
|
||||
}
|
||||
|
||||
return weight;
|
||||
}
|
||||
|
||||
$(function() {
|
||||
var autosave_timeout_id = null;
|
||||
if ($('form[data-has-draft]').length == 1) {
|
||||
|
@ -30,6 +107,48 @@ $(function() {
|
|||
last_auto_save = $('form[data-has-draft]').serialize();
|
||||
});
|
||||
}
|
||||
var well_known_domains = ['gmail.com', 'msn.com', 'hotmail.com', 'hotmail.fr', 'wanadoo.fr',
|
||||
'free.fr', 'yahoo.fr', 'numericable.fr', 'laposte.fr', 'orange.fr'];
|
||||
$('input[type=email]').on('change wcs:change', function() {
|
||||
var $email_input = $(this);
|
||||
var val = $email_input.val();
|
||||
var val_domain = val.split('@')[1];
|
||||
var $domain_hint_div = this.domain_hint_div;
|
||||
var highest_ratio = 0;
|
||||
var suggestion = null;
|
||||
for (var i=0; i < well_known_domains.length; i++) {
|
||||
var domain = well_known_domains[i];
|
||||
var ratio = val_domain.similarity(domain);
|
||||
if (ratio > highest_ratio) {
|
||||
highest_ratio = ratio;
|
||||
suggestion = domain;
|
||||
}
|
||||
}
|
||||
if (highest_ratio > 0.80 && highest_ratio < 1) {
|
||||
if ($domain_hint_div === undefined) {
|
||||
$domain_hint_div = $('<div class="field-live-hint"><span class="message"></span><button class="action"></button><button class="close"><span class="sr-only"></span></button></div>');
|
||||
this.domain_hint_div = $domain_hint_div;
|
||||
$(this).after($domain_hint_div);
|
||||
$domain_hint_div.find('button.action').on('click', function() {
|
||||
$email_input.val($email_input.val().replace(/@.*/, '@' + $(this).data('suggestion')));
|
||||
$email_input.trigger('wcs:change');
|
||||
$domain_hint_div.hide();
|
||||
return false;
|
||||
});
|
||||
$domain_hint_div.find('button.close').on('click', function() {
|
||||
$domain_hint_div.hide();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
$domain_hint_div.find('span').text(WCS_I18N.email_domain_suggest + ' @' + suggestion + ' ?');
|
||||
$domain_hint_div.find('button.action').text(WCS_I18N.email_domain_fix);
|
||||
$domain_hint_div.find('button.action').data('suggestion', suggestion);
|
||||
$domain_hint_div.find('button.close span.sr-only').text(WCS_I18N.close);
|
||||
$domain_hint_div.show();
|
||||
} else if ($domain_hint_div) {
|
||||
$domain_hint_div.hide();
|
||||
}
|
||||
});
|
||||
$('.date-pick').each(function() {
|
||||
if (this.type == "date" || this.type == "time") {
|
||||
return; // prefer native date/time widgets
|
||||
|
|
|
@ -411,6 +411,8 @@ class RootDirectory(Directory):
|
|||
's2_loadmore': _('Loading more results...'),
|
||||
's2_searching': _('Searching...'),
|
||||
'close': _('Close'),
|
||||
'email_domain_suggest': _('Did you want to write'),
|
||||
'email_domain_fix': _('Apply fix'),
|
||||
}
|
||||
return 'WCS_I18N = %s;\n' % json.dumps(strings)
|
||||
|
||||
|
|
Loading…
Reference in New Issue